web-dev-qa-db-ja.com

リストビューで複数のカウントダウンタイマーを処理する方法

(カスタムリストアダプターを使用した)リストビューがあります。すべての行にカウントダウンを表示する必要があります。

たとえば、リストに4つのアイテムが含まれている場合、4つの行があります。この時点では、時間が異なるため、4つの異なるカウントダウン(行ごとに1つ)を処理する必要があります。

enter image description here

これまでのところ、私は次のように処理しています:カスタムリストアダプターのgetView()メソッド内で、新しいCountDownTimerを作成し、TextView内に残り時間を表示します。

しかし問題は、アクティビティが大幅に遅くなることです。最悪の場合でも正しくスクロールできません(行が表示されるたびに新しいCountDownTimerが作成されるため)。

私はより良い解決策を求めてたくさん検索しましたが、誰も満足していませんでした。

ListView内の複数のカウントダウンタイマーを処理するためのよりクリーンでスムーズなソリューションはありますか?

ありがとう

17
Rob

すべての残り時間を表示しようとする代わりに、表示されているアイテムの残り時間を更新するのがアイデアです。

次のサンプルコードに従って、お知らせください。

主な活動 :

public class MainActivity extends Activity {

private ListView lvItems;
private List<Product> lstProducts;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    lvItems = (ListView) findViewById(R.id.lvItems);
    lstProducts = new ArrayList<>();
    lstProducts.add(new Product("A", System.currentTimeMillis() + 10000));
    lstProducts.add(new Product("B", System.currentTimeMillis() + 20000));
    lstProducts.add(new Product("C", System.currentTimeMillis() + 20000));
    lstProducts.add(new Product("D", System.currentTimeMillis() + 20000));
    lstProducts.add(new Product("E", System.currentTimeMillis() + 20000));
    lstProducts.add(new Product("F", System.currentTimeMillis() + 20000));
    lstProducts.add(new Product("G", System.currentTimeMillis() + 30000));
    lstProducts.add(new Product("H", System.currentTimeMillis() + 20000));
    lstProducts.add(new Product("I", System.currentTimeMillis() + 20000));
    lstProducts.add(new Product("J", System.currentTimeMillis() + 40000));
    lstProducts.add(new Product("K", System.currentTimeMillis() + 20000));
    lstProducts.add(new Product("L", System.currentTimeMillis() + 50000));
    lstProducts.add(new Product("M", System.currentTimeMillis() + 60000));
    lstProducts.add(new Product("N", System.currentTimeMillis() + 20000));
    lstProducts.add(new Product("O", System.currentTimeMillis() + 10000));

    lvItems.setAdapter(new CountdownAdapter(MainActivity.this, lstProducts));
}

private class Product {
    String name;
    long expirationTime;

    public Product(String name, long expirationTime) {
        this.name = name;
        this.expirationTime = expirationTime;
    }
}


public class CountdownAdapter extends ArrayAdapter<Product> {

    private LayoutInflater lf;
    private List<ViewHolder> lstHolders;
    private Handler mHandler = new Handler();
    private Runnable updateRemainingTimeRunnable = new Runnable() {
        @Override
        public void run() {
            synchronized (lstHolders) {
                long currentTime = System.currentTimeMillis();
                for (ViewHolder holder : lstHolders) {
                    holder.updateTimeRemaining(currentTime);
                }
            }
        }
    };

    public CountdownAdapter(Context context, List<Product> objects) {
        super(context, 0, objects);
        lf = LayoutInflater.from(context);
        lstHolders = new ArrayList<>();
        startUpdateTimer();
    }

    private void startUpdateTimer() {
        Timer tmr = new Timer();
        tmr.schedule(new TimerTask() {
            @Override
            public void run() {
                mHandler.post(updateRemainingTimeRunnable);
            }
        }, 1000, 1000);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder = null;
        if (convertView == null) {
            holder = new ViewHolder();
            convertView = lf.inflate(R.layout.list_item, parent, false);
            holder.tvProduct = (TextView) convertView.findViewById(R.id.tvProduct);
            holder.tvTimeRemaining = (TextView) convertView.findViewById(R.id.tvTimeRemaining);
            convertView.setTag(holder);
            synchronized (lstHolders) {
                lstHolders.add(holder);
            }
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        holder.setData(getItem(position));

        return convertView;
    }
}

private class ViewHolder {
    TextView tvProduct;
    TextView tvTimeRemaining;
    Product mProduct;

    public void setData(Product item) {
        mProduct = item;
        tvProduct.setText(item.name);
        updateTimeRemaining(System.currentTimeMillis());
    }

    public void updateTimeRemaining(long currentTime) {
        long timeDiff = mProduct.expirationTime - currentTime;
        if (timeDiff > 0) {
            int seconds = (int) (timeDiff / 1000) % 60;
            int minutes = (int) ((timeDiff / (1000 * 60)) % 60);
            int hours = (int) ((timeDiff / (1000 * 60 * 60)) % 24);
            tvTimeRemaining.setText(hours + " hrs " + minutes + " mins " + seconds + " sec");
        } else {
            tvTimeRemaining.setText("Expired!!");
        }
    }
}
}

activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:layout_width="match_parent"
Android:layout_height="match_parent">

<ListView
    Android:id="@+id/lvItems"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent" />

</RelativeLayout>

list_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:orientation="vertical"
Android:padding="5dp">

<TextView
    Android:id="@+id/tvProduct"
    Android:layout_width="match_parent"
    Android:layout_height="wrap_content"
    Android:layout_margin="10dp"
    Android:text="Product Name"
    Android:textSize="16dp"
    Android:textStyle="bold" />

<TextView
    Android:id="@+id/tvTimeRemaining"
    Android:layout_width="match_parent"
    Android:layout_height="wrap_content"
    Android:layout_margin="10dp"
    Android:text="Time Remaining : " />

</LinearLayout>
34
Eldhose M Babu

遅いかもしれませんが、これは@Eldhose M Babuの優れた回答に基づくphpとjsonを使用したrecyclerViewバージョンです。お役に立てれば幸いです:)

Multiple countdown photo display

Adapter.Java

    public class Adapter extends RecyclerView.Adapter<Adapter.ViewHolder> {

    private Context context;
    private final List<ViewHolder> lstHolders;
    public List<Model> lst;

    private Handler mHandler = new Handler();
    private Runnable updateRemainingTimeRunnable = new Runnable() {
        @Override
        public void run() {
            synchronized (lstHolders) {
                long currentTime = System.currentTimeMillis();
                for (ViewHolder holder : lstHolders) {
                    holder.updateTimeRemaining(currentTime);
                }
            }
        }
    };

    public Adapter(List<Model> lst, Context context){
        super();
        this.lst = lst;
        this.context = context;
        lstHolders = new ArrayList<>();
        startUpdateTimer();
    }

    private void startUpdateTimer() {
        Timer tmr = new Timer();
        tmr.schedule(new TimerTask() {
            @Override
            public void run() {
                mHandler.post(updateRemainingTimeRunnable);
            }
        }, 1000, 1000);
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list, parent, false);
        ViewHolder viewHolder = new ViewHolder(v);
        return viewHolder;
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        holder.setData(lst.get(position));
        synchronized (lstHolders) {
            lstHolders.add(holder);
        }
        holder.updateTimeRemaining(System.currentTimeMillis());
    }

    @Override
    public int getItemCount() {
        return lst.size();
    }

    class ViewHolder extends RecyclerView.ViewHolder{
        public TextView textViewName;
        public TextView tvTimeRemaining;
        Model mModel;

        public void setData(Model item) {
            mModel = item;
            textViewName.setText(item.name);
            updateTimeRemaining(System.currentTimeMillis());
        }

        public void updateTimeRemaining(long currentTime) {
            long timeDiff = mModel.expirationTime - currentTime;
            if (timeDiff > 0) {
                int seconds = (int) (timeDiff / 1000) % 60;
                int minutes = (int) ((timeDiff / (1000 * 60)) % 60);
                int hours = (int) ((timeDiff / (1000 * 60 * 60)) % 24);

                tvTimeRemaining.setText(hours + " hrs " + minutes + " mins " + seconds + " sec");
            } else {
                tvTimeRemaining.setText("Expired!!");
            }
        }

        public ViewHolder(View itemView) {
            super(itemView);
            tvTimeRemaining = (TextView) itemView.findViewById(R.id.cd);
            textViewName = (TextView) itemView.findViewById(R.id.textViewName);
        }
    }
}

MainActivity.Java

public class MainActivity extends AppCompatActivity {

private List<Model> lst;
private RecyclerView recyclerView;
private RecyclerView.LayoutManager layoutManager;
private RecyclerView.Adapter adapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);


    recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
    recyclerView.setHasFixedSize(true);
    layoutManager = new LinearLayoutManager(this);
    recyclerView.setLayoutManager(layoutManager);
    lst = new ArrayList<>();
    getData();


    FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
    fab.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG).setAction("Action", null).show();
        }
    });
}

private void getData(){
    final ProgressDialog loading = ProgressDialog.show(this,"Loading Data", "Please wait...",false,false);

    JsonArrayRequest jsonArrayRequest = new JsonArrayRequest("http://192.168.200.102/api.php",
            new Response.Listener<JSONArray>() {
                @Override
                public void onResponse(JSONArray response) {
                    loading.dismiss();
                    parseData(response);
                }
            },
            new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {

                }
            });

    RequestQueue requestQueue = Volley.newRequestQueue(this);
    requestQueue.add(jsonArrayRequest);
}

private void parseData(JSONArray array){
    for(int i = 0; i<array.length(); i++) {
        Model model = new Model();
        JSONObject json = null;
        try {
            json = array.getJSONObject(i);
            model.setexpirationTime(json.getLong("expirationTime"));
            model.setName(json.getString("name"));
        } catch (JSONException e) {
            e.printStackTrace();
        }
        lst.add(model);
    }

    adapter = new Adapter(lst, this);
    recyclerView.setAdapter(adapter);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.menu_main, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    int id = item.getItemId();

    if (id == R.id.action_settings) {
        return true;
    }

    return super.onOptionsItemSelected(item);
}}

Model.Java

public class Model {
public String name;
public long expirationTime;

public long getexpirationTime() {
    return expirationTime;
}

public void setexpirationTime(long expire) {
    this.expirationTime = expire;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<Android.support.design.widget.CoordinatorLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:app="http://schemas.Android.com/apk/res-auto"
    xmlns:tools="http://schemas.Android.com/tools"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:fitsSystemWindows="true"
    tools:context="here.math.MainActivity">

    <Android.support.design.widget.AppBarLayout
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content"
        Android:theme="@style/AppTheme.AppBarOverlay">

        <Android.support.v7.widget.Toolbar
            Android:id="@+id/toolbar"
            Android:layout_width="match_parent"
            Android:layout_height="?attr/actionBarSize"
            Android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

    </Android.support.design.widget.AppBarLayout>

    <include layout="@layout/content_main" />

    <Android.support.design.widget.FloatingActionButton
        Android:id="@+id/fab"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:layout_gravity="bottom|end"
        Android:layout_margin="@dimen/fab_margin"
        Android:src="@Android:drawable/ic_dialog_email" />

</Android.support.design.widget.CoordinatorLayout>

content_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:app="http://schemas.Android.com/apk/res-auto"
    xmlns:tools="http://schemas.Android.com/tools"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:paddingBottom="@dimen/activity_vertical_margin"
    Android:paddingLeft="@dimen/activity_horizontal_margin"
    Android:paddingRight="@dimen/activity_horizontal_margin"
    Android:paddingTop="@dimen/activity_vertical_margin"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="here.math.MainActivity"
    tools:showIn="@layout/activity_main">

        <Android.support.v7.widget.RecyclerView
            Android:id="@+id/recyclerView"
            Android:layout_width="match_parent"
            Android:layout_height="wrap_content">
        </Android.support.v7.widget.RecyclerView>
</RelativeLayout>

list.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent">

    <Android.support.v7.widget.CardView
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content"
        Android:layout_marginBottom="20dp">

        <LinearLayout
            Android:padding="@dimen/activity_horizontal_margin"
            Android:orientation="vertical"
            Android:layout_width="match_parent"
            Android:layout_height="match_parent">


            <TableLayout
                Android:layout_width="match_parent"
                Android:layout_height="wrap_content">

                <TableRow>
                    <TextView
                        Android:text="Expire time"
                        Android:paddingRight="10dp"
                        Android:layout_width="wrap_content"
                        Android:layout_height="wrap_content" />

                    <TextView
                        Android:id="@+id/cd"
                        Android:textStyle="bold"
                        Android:layout_width="wrap_content"
                        Android:layout_height="wrap_content" />

                </TableRow>

                <TableRow>
                    <TextView
                        Android:text="Name"
                        Android:paddingRight="10dp"
                        Android:layout_width="wrap_content"
                        Android:layout_height="wrap_content" />

                    <TextView
                        Android:id="@+id/textViewName"
                        Android:textStyle="bold"
                        Android:layout_width="wrap_content"
                        Android:layout_height="wrap_content" />

                </TableRow>

            </TableLayout>
        </LinearLayout>
    </Android.support.v7.widget.CardView>

</RelativeLayout>

そして、URL部分については、あなたは何かをすることができます:

<?php

$arr = array(
array(
        'name' => "Android",
        'expirationTime' => 1456860322 * 1000, // timestamp multiply for display in milliseconds
),
array(
        'name' => "Android 2",
        'expirationTime' => 1456900522 * 1000,
),
array(
        'name' => "Android 3",
        'expirationTime' =>  1459509819 * 1000,
),
array(
        'name' => "Android 4",
        'expirationTime' => 1457021950 * 1000,
),
);

echo json_encode($arr);
15
Hamed Okhovvat

listview Androidのカウントダウンタイマー の例をご覧ください。

カスタムアダプタ

import Android.os.CountDownTimer;
import Android.support.v7.widget.RecyclerView;
import Android.util.Log;
import Android.view.LayoutInflater;
import Android.view.View;
import Android.view.ViewGroup;
import Android.widget.TextView;

import Java.util.ArrayList;
public class Adapter extends RecyclerView.Adapter<Adapter.MyViewHolder>{

private ArrayList<String> al_data;

public class MyViewHolder extends RecyclerView.ViewHolder
{
    public TextView tv_timer;
    CountDownTimer timer;

    public MyViewHolder (View view){
        super(view);
        tv_timer = (TextView)view.findViewById(R.id.tv_timer);

    }


}

public Adapter(ArrayList<String> al_data) {
    this.al_data = al_data;
}

@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_layout,parent,false);


    return new MyViewHolder(view);
}

@Override
public void onBindViewHolder(final MyViewHolder holder, int position) {

    holder.tv_timer.setText(al_data.get(position));

    if (holder.timer != null) {
        holder.timer.cancel();
    }
     long timer = Long.parseLong(al_data.get(position));

    timer = timer*1000;

    holder.timer = new CountDownTimer(timer, 1000) {
        public void onTick(long millisUntilFinished) {
          holder.tv_timer.setText("" + millisUntilFinished/1000 + " Sec");
        }

        public void onFinish() {
            holder.tv_timer.setText("00:00:00");
        }
    }.start();


}

@Override
public int getItemCount() {
    return al_data.size();
}



}

主な活動

package com.androidsolutionworld.multipletimer;

import Android.support.v7.app.AppCompatActivity;
import Android.os.Bundle;
import Android.support.v7.widget.LinearLayoutManager;
import Android.support.v7.widget.RecyclerView;
import Android.widget.LinearLayout;

import Java.util.ArrayList;

public class MainActivity extends AppCompatActivity {
private RecyclerView recyclerView;
private ArrayList<String> al_data = new ArrayList<>();
private Adapter obj_adapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    recyclerView = (RecyclerView)findViewById(R.id.recycler_view);
    al_data.add("1234");
    al_data.add("1257");
    al_data.add("100");
    al_data.add("1547");
    al_data.add("200");
    al_data.add("500");
    al_data.add("2000");
    al_data.add("1000");

    obj_adapter = new Adapter(al_data);
    LinearLayoutManager layoutManager = new LinearLayoutManager(getApplicationContext(),LinearLayoutManager.VERTICAL,false);
    recyclerView.setLayoutManager(layoutManager);
    recyclerView.setAdapter(obj_adapter);
}
}

ありがとう

1
Dev

アダプタークラス:-


パブリッククラスCountdownAdapter extends RecyclerView.Adapter {ArrayList mList;コンテキストmContext;

public CountdownAdapter(ArrayList<Long> mList, Context mContext) {
    this.mList = mList;
    this.mContext = mContext;
}

public class ViewHolder extends RecyclerView.ViewHolder {
    CountDownTimer timerCount;
    TextView mCountDownTxt;

    public ViewHolder(View convertView) {
        super(convertView);
        mCountDownTxt = (TextView)convertView.findViewById(R.id.countdown_text);
    }
}

@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    return null;
}

@Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
    if(holder.timerCount==null) {
        holder.timerCount = new CountDownTimer(mList.get(position), 1000) {
            @Override
            public void onTick(long millis) {
                String hms = String.format("%02d:%02d",  TimeUnit.MILLISECONDS.toMinutes(millis) - TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(millis)), TimeUnit.MILLISECONDS.toSeconds(millis) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis)));
                holder.mCountDownTxt.setText(hms);
            }

            @Override
            public void onFinish() {

            }
        };
    }
    holder.mCountDownTxt.setVisibility(View.VISIBLE);
    holder.timerCount.start();
}

@Override
public int getItemCount() {
    return mList.size();
}

}

1
Imeshke