android listview 多个item 倒计时怎么做好
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了android listview 多个item 倒计时怎么做好相关的知识,希望对你有一定的参考价值。
Handler+Timer+TimerTask实现:Activity中代码:
public class ShopActivity extends Activity
private ListView lv;
private ShopAdapter pmAdapter;
private Timer timer = new Timer();
private TimerTask timerTask;
private List<MyData> dataList;
@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler()
public void handleMessage(android.os.Message msg)
if (msg.what == 1)
for (int i = 0; i < mMyMatchList.size(); i++)
dataList.get(i).setServerDate(Long.valueOf(dataList.get(i).getServerDate())+ 1 + "");//这是服务器当前时间,以此时间为基数进行倒时计,因为服务器时间以秒为单位返回,所以我们每隔1秒,把当前服务器时间+1,也可以把结束时间-1
serverTime = Long.valueOf(mMyMatchList.get(i).getServerDate());
pmAdapter.notifyDataSetChanged();
;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_shop);
dataList=new ArrayList<MyData>();//接下来要从服务端取到数据,加入dataList中
lv= (ListView) this.findViewById(R.id.shop_lv);
pmAdapter = new ShopAdapter(ShopActivity.this, this);
lv.setAdapter(pmAdapter);
timerTask = new TimerTask()
@Override
public void run()
Message message = Message.obtain();
message.what = 1;
mHandler.sendMessage(message);
;
timer.schedule(timerTask, 1000, 1000);
@Override
protected void onResume()
super.onResume();
@Override
protected void onDestroy()
if (null != timer)
timer.cancel();
super.onDestroy();
最后在适配器类ShopAdapter中,根据服务器返回的截止时间和服务器时间,把数据加上去就可以了:
TextView timeTv=findViewById();
timeTv.setText(getTimeFromInt(结束时间-服务器时间));
getTimeFromInt这个方法是计算时间差并转换成所需要格式的:
public String getTimeFromInt(long time)
if (time <= 0) return "已结束";
long day = time / (1 * 60 * 60 * 24);
long hour = time / (1 * 60 * 60) % 24;
long minute = time / (1 * 60) % 60;
long second = time / (1) % 60;
return "还剩:" + day + "天" + hour + "小时" + minute + "分" + second + "秒";
参考技术A 基本思路:
使用CountDownTimer来完成基本倒计时功能
按倒计时的时间间隔来分组管理CountDownTimer, 即相同时间间隔的Item使用同一个CountDownTimer
每组CountDownTimer倒计时的时间取组内的最大值, 一旦Item到达自身的倒计时时间, 就会从该组倒计时中被移除
定义一个倒计时任务, 用来管理上述分组
每个业务可以根据需要创建并启动多个倒计时任务, 且可以在适当的页面生命周期函数中停止该任务
Android SlideAndDragListView,一个可排序可滑动item的ListView
SlideAndDragListView简介
SlideAndDragListView,可排序、可滑动item显示”菜单”的ListView。
SlideAndDragListView(SDLV)继承于Android的ListView,SDLV可以拖动item到SDLV的任意位置,其中包括了拖动item往上滑和往下滑;SDLV可以向右滑动item,像Android的QQ那样(QQ是向左滑),然后显现出来"菜单”之类的按钮。
github地址:https://github.com/yydcdut/SlideAndDragListView
开源中国:http://git.oschina.net/yydcdut/SlideAndDragListView
怎么使用
XML
<com.yydcdut.sdlv.SlideAndDragListView xmlns:sdlv="http://schemas.android.com/apk/res-auto" android:layout_width="fill_parent" android:layout_height="fill_parent" android:divider="@android:color/black" android:dividerHeight="0.5dip" android:paddingLeft="8dip" android:paddingRight="8dip" sdlv:item_background="@android:color/white" sdlv:item_btn1_background="@drawable/btn1_drawable" sdlv:item_btn1_text="Delete1" sdlv:item_btn1_text_color="#00ff00" sdlv:item_btn2_background="@drawable/btn2_drawable" sdlv:item_btn2_text="Rename1" sdlv:item_btn2_text_color="#ff0000" sdlv:item_btn_number="2" sdlv:item_btn_width="70dip" sdlv:item_height="80dip"> </com.yydcdut.sdlv.SlideAndDragListView>
attributes
item_background
- item滑开那部分的背景。
item_btn1_background
- "菜单"中第一个button的背景。
item_btn1_text
- "菜单"中第一个button的text。
item_btn1_text_color
- "菜单"中第一个button的字体颜色。
item_btn2_background
- "菜单"中第二个button的背景。
item_btn2_text
- "菜单"中第二个button的text。
item_btn2_text_color
- "菜单"中第二个button的字体颜色。
item_btn_number
- 要显示出来的”菜单”中的button的个数,在0~2之间。
item_btn_width
- “菜单”中button的宽度。
item_height
- item的高度。
监听器
SlideAndDragListView.OnListItemLongClickListener
sdlv.setOnListItemLongClickListener(new SlideAndDragListView.OnListItemLongClickListener() { @Override public void onListItemLongClick(View view, int position) { } });
public void onListItemLongClick(View view, int position)
. 参数 view
是被长点击的item, 参数 position
是item在SDLV中的位置。
SlideAndDragListView.OnListItemClickListener
sdlv.setOnListItemClickListener(new SlideAndDragListView.OnListItemClickListener() { @Override public void onListItemClick(View v, int position) { } });
public void onListItemClick(View view, int position)
. 参数 view
是被点击的item, 参数 position
是item在SDLV中的位置。
SlideAndDragListView.OnDragListener
sdlv.setOnDragListener(new SlideAndDragListView.OnDragListener() { @Override public void onDragViewMoving(int position) { } @Override public void onDragViewDown(int position) { } });
public void onDragViewMoving(int position)
.参数 position
是被拖动的item的现在所在的位置,同时onDragViewMoving这个方法会被不停的调用,因为一直在拖动,同时position也会改变。
public void onDragViewDown(int position)
. 参数 position
是被拖动的item被放下的时候在SDLV中的位置。
SlideAndDragListView.OnSlideListener
sdlv.setOnSlideListener(new SlideAndDragListView.OnSlideListener() { @Override public void onSlideOpen(View view, int position) { } @Override public void onSlideClose(View view, int position) { } });
public void onSlideOpen(View view, int position)
. 参数 view
是滑动开的那个item, 同时 position
是那个item在SDLV中的位置。
public void onSlideClose(View view, int position)
.参数 view
是滑动关闭的那个item, 同时 position
是那个item在SDLV中的位置。
SlideAndDragListView.OnButtonClickListenerProxy
sdlv.setOnButtonClickListenerProxy(new SlideAndDragListView.OnButtonClickListenerProxy() { @Override public void onClick(View view, int position, int number) { } });
public void onClick(View view, int position, int number)
. 参数 view
是”菜单”中被点击的button,参数 position
这个button所在的item在SDLV中的位置,参数, number
代表哪一个被点击了,因为可能会有2个。
权限
<uses-permission android:name="android.permission.VIBRATE"/>
简单的实现
SDLV用的是最基本的Android API来实现的,所以很好理解。其中各个功能的实现分别是:
- 拖动item:Android的View.OnDragListener接口。
- 向右滑动item显示”菜单”:Android的Scroller类和View的scrollTo方法。
- 拖动item往上或往下:ListView的smoothScrollToPosition方法。
- 适配器:BaseAdapter类和ViewHolder。
- item的长点击事件:因为系统的onItemLongClick事件与View.OnDragListener接口中的事件有冲突,所以我SDLV中通过Handler在手势事件中发送Message模拟onItemLongClick事件。
- 模拟onItemLongClick中的振动:Context.VIBRATOR_SERVICE。
- 手势事件:系统的dispatchTouchEvent。
结构
各个击破
里面有几个SDItemXXXX的控件,主要是应对于item的高度而重写了onMeasure
方法。这里就不说了哈。
从layout布局开始说吧:
item_sdlv.xml
<?xml version="1.0" encoding="utf-8"?> <com.yydcdut.sdlv.SDItemLayout android:id="@+id/layout_item_main" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:ignore="MissingPrefix"> <com.yydcdut.sdlv.SDItemLayout android:id="@+id/layout_item_bg" android:layout_width="fill_parent" android:layout_height="@dimen/slv_item_height" android:background="@android:color/transparent"> <com.yydcdut.sdlv.SDItemBGImage android:id="@+id/img_item_bg" android:layout_width="fill_parent" android:layout_height="@dimen/slv_item_height" android:background="@android:color/white"/> <com.yydcdut.sdlv.SDItemText android:id="@+id/txt_item_edit_btn1" android:layout_width="@dimen/slv_item_bg_btn_width" android:layout_height="@dimen/slv_item_height" android:layout_alignParentLeft="true" android:background="@android:color/holo_red_light" android:gravity="center" android:lines="1" android:text="@string/btn1" android:textColor="@android:color/white" android:textSize="@dimen/txt_size"/> <com.yydcdut.sdlv.SDItemText android:id="@+id/txt_item_edit_btn2" android:layout_width="@dimen/slv_item_bg_btn_width" android:layout_height="@dimen/slv_item_height" android:layout_toRightOf="@+id/txt_item_edit_btn1" android:background="@android:color/darker_gray" android:gravity="center" android:lines="1" android:text="@string/btn2" android:textColor="@android:color/white" android:textSize="@dimen/txt_size"/> </com.yydcdut.sdlv.SDItemLayout> <com.yydcdut.sdlv.SDItemLayout android:id="@+id/layout_item_scroll" android:layout_width="match_parent" android:layout_height="@dimen/slv_item_height" android:background="@android:color/transparent"> <com.yydcdut.sdlv.SDItemBGImage android:id="@+id/img_item_scroll_bg" android:layout_width="fill_parent" android:layout_height="@dimen/slv_item_height" android:background="@android:color/white"/> <FrameLayout android:id="@+id/layout_custom" android:layout_width="fill_parent" android:layout_height="@dimen/slv_item_height"> </FrameLayout> </com.yydcdut.sdlv.SDItemLayout> </com.yydcdut.sdlv.SDItemLayout>
根是一个RelativeLayout,里面有两个大的RelativeLayout子跟。底层那个RelativeLayout是有三个控件,分别是一个长度宽度都和父Layout一样的ImageView,这个是就前面讲的item_background的背景设置的地方,另外两个是TextView,就是前面讲到的”菜单”中的button。上面那层也有个ImageView,主要是覆盖住下面那层Layout,因为什么不直接用Layout的background呢,因为当时发现scrollTo之后下面那层是没有显示出来的,还是被挡住了的。另外一个是一个FrameLayout,这里是用户自定义的item显示的地方。
看完了item的布局,那么来看看Adapter吧。
public abstract class SDAdapter<T> extends BaseAdapter implements View.OnClickListener { /* 上下文 */ private final Context mContext; /* 数据 */ private List<T> mDataList; /* Drag的位置 */ private int mDragPosition = -1; /* 点击button的位置 */ private int mBtnPosition = -1; /* button的单击监听器 */ private OnButtonClickListener mOnButtonClickListener; /* 当前滑开的item的位置 */ private int mSlideOpenItemPosition; /* ---------- attrs ----------- */ private float mItemHeight; private int mItemBtnNumber; private String mItemBtn1Text; private String mItemBtn2Text; private float mItemBtnWidth; private Drawable mItemBGDrawable; private int mItemBtn1TextColor; private int mItemBtn2TextColor; private Drawable mItemBtn1Drawable; private Drawable mItemBtn2Drawable; /* ---------- attrs ----------- */ public SDAdapter(Context context, List<T> dataList) { mContext = context; mDataList = dataList; } @Override public int getCount() { return mDataList.size(); } @Override public Object getItem(int position) { return mDataList.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView == null) { holder = new ViewHolder(); convertView = LayoutInflater.from(mContext).inflate(R.layout.item_sdlv, null); holder.layoutMain = (SDItemLayout) convertView.findViewById(R.id.layout_item_main); holder.layoutMain.setItemHeight((int) mItemHeight); holder.layoutScroll = (SDItemLayout) convertView.findViewById(R.id.layout_item_scroll); holder.layoutScroll.setItemHeight((int) mItemHeight); holder.layoutBG = (SDItemLayout) convertView.findViewById(R.id.layout_item_bg); holder.layoutBG.setItemHeight((int) mItemHeight); holder.imgBGScroll = (SDItemBGImage) convertView.findViewById(R.id.img_item_scroll_bg); holder.imgBGScroll.setItemHeight((int) mItemHeight); holder.imgBG = (SDItemBGImage) convertView.findViewById(R.id.img_item_bg); holder.imgBG.setItemHeight((int) mItemHeight); holder.layoutCustom = (FrameLayout) convertView.findViewById(R.id.layout_custom); holder.btn1 = (SDItemText) convertView.findViewById(R.id.txt_item_edit_btn1); holder.btn2 = (SDItemText) convertView.findViewById(R.id.txt_item_edit_btn2); holder.btn1.setBtnWidth((int) mItemBtnWidth); holder.btn1.setBtnHeight((int) mItemHeight); holder.btn2.setBtnWidth((int) mItemBtnWidth); holder.btn2.setBtnHeight((int) mItemHeight); //如果用户设置了背景的话就用用户的背景 if (mItemBGDrawable != null) { holder.imgBG.setBackgroundDrawable(mItemBGDrawable); holder.imgBGScroll.setBackgroundDrawable(mItemBGDrawable); } //判断哪些隐藏哪些显示 checkVisible(holder); //设置text holder.btn1.setText(mItemBtn1Text);//setText有容错处理 holder.btn2.setText(mItemBtn2Text);//setText有容错处理 //设置监听器 holder.btn1.setOnClickListener(this); holder.btn2.setOnClickListener(this); //一开始加载的时候都不可点击 holder.btn1.setClickable(false); holder.btn2.setClickable(false); //背景和字体颜色 holder.btn1.setBackgroundDrawable(mItemBtn1Drawable); holder.btn2.setBackgroundDrawable(mItemBtn2Drawable); holder.btn1.setTextColor(mItemBtn1TextColor); holder.btn2.setTextColor(mItemBtn2TextColor); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } //没有展开的item里面的btn是不可点击的 if (mSlideOpenItemPosition == position) { holder.btn1.setClickable(true); holder.btn2.setClickable(true); } else { holder.btn1.setClickable(false); holder.btn2.setClickable(false); } //用户的view View customView = getView(mContext, holder.layoutCustom.getChildAt(0), position, mDragPosition); if (holder.layoutCustom.getChildAt(0) == null) { holder.layoutCustom.addView(customView); } else { holder.layoutCustom.removeViewAt(0); holder.layoutCustom.addView(customView); } //所有的都归位 holder.layoutScroll.scrollTo(0, 0); //把背景显示出来(因为在drag的时候会将背景透明,因为好看) holder.imgBGScroll.setVisibility(View.VISIBLE); holder.layoutBG.setVisibility(View.VISIBLE); return convertView; } /** * 与BaseAdapter类似 * * @param context * @param convertView * @param position * @param dragPosition 当前拖动的item的位置,如果没有拖动item的话值是-1 * @return */ public abstract View getView(Context context, View convertView, int position, int dragPosition); @Override public void onClick(View v) { if (v.getId() == R.id.txt_item_edit_btn1) { if (mOnButtonClickListener != null && mBtnPosition != -1) { mOnButtonClickListener.onClick(v, mBtnPosition, 0); } } else if (v.getId() == R.id.txt_item_edit_btn2) { if (mOnButtonClickListener != null && mBtnPosition != -1) { mOnButtonClickListener.onClick(v, mBtnPosition, 1); } } } class ViewHolder { public SDItemLayout layoutMain; public SDItemLayout layoutScroll; public SDItemLayout layoutBG; public SDItemBGImage imgBGScroll; public SDItemBGImage imgBG; public SDItemText btn1; public SDItemText btn2; public FrameLayout layoutCustom; } /** * 判断用户要几个button * * @param vh */ private void checkVisible(ViewHolder vh) { switch (mItemBtnNumber) { case 0: vh.btn1.setVisibility(View.GONE); vh.btn2.setVisibility(View.GONE); break; case 1: vh.btn1.setVisibility(View.VISIBLE); vh.btn2.setVisibility(View.GONE); break; case 2: vh.btn1.setVisibility(View.VISIBLE); vh.btn2.setVisibility(View.VISIBLE); break; default: throw new IllegalArgumentException(""); } vh.btn1.setClickable(false); vh.btn2.setClickable(false); } //............................... }
Adapter里面的作用就是把item的layout显示出来,然后设置高度,某些控件需要设置宽度,然后设置一些其他参数,比如背景啊等等。其中要注意的是holder.btn1.setClickable(false);
和 holder.btn2.setClickable(false);
,因为不设置clickable为false的话就出当看不见的时间点击那个位置也会触发onClick事件。第二个就是:holder.layoutScroll.scrollTo(0, 0);
这个地方,当ListView滑走的时候就把这个归位回到0,0的位置,不然回出现顺序错乱。第三个地方是:
//用户的view View customView = getView(mContext, holder.layoutCustom.getChildAt(0), position, mDragPosition); if (holder.layoutCustom.getChildAt(0) == null) { holder.layoutCustom.addView(customView); } else { holder.layoutCustom.removeViewAt(0); holder.layoutCustom.addView(customView); }
这里的customView是通过一个abstract方法,用户只需要实现这个Adapter中的这个方法就行了。其次就是getChildAt、addView和removeViewAt这三个方法,主要是不同的position有显示不同的用户的信息。
在onClick事件中要去判断当前点击的是不是已经在item中显现出来的,是的话才回掉出去。
接下来讲讲SDLV吧,我把重要部分的代码贴出来。
public class SlideAndDragListView<T> extends ListView implements Handler.Callback, View.OnDragListener, SDAdapter.OnButtonClickListener, AdapterView.OnItemClickListener { //.................... /* onTouch里面的状态 */ private static final int STATE_NOTHING = -1;//抬起状态 private static final int STATE_DOWN = 0;//按下状态 private static final int STATE_LONG_CLICK = 1;//长点击状态 private static final int STATE_SCROLL = 2;//SCROLL状态 private static final int STATE_LONG_CLICK_FINISH = 3;//长点击已经触发完成 private int mState = STATE_NOTHING; //..................... @Override public boolean handleMessage(Message msg) { switch (msg.what) { case MSG_WHAT_LONG_CLICK: if (mState == STATE_LONG_CLICK) {//如果得到msg的时候state状态是Long Click的话 //改为long click触发完成 mState = STATE_LONG_CLICK_FINISH; //得到长点击的位置 int position = msg.arg1; //找到那个位置的view View view = getChildAt(mSlideTargetPosition - getFirstVisiblePosition()); //通知adapter mSDAdapter.setDragPosition(position); //如果设置了监听器的话,就触发 if (mOnListItemLongClickListener != null) { scrollBack(); mVibrator.vibrate(100); mOnListItemLongClickListener.onListItemLongClick(view, position); } mCurrentPosition = position; mBeforeCurrentPosition = position; mBeforeBeforePosition = position; //把背景给弄透明,这样drag的时候要好看些 view.findViewById(R.id.layout_item_bg).setVisibility(INVISIBLE); view.findViewById(R.id.img_item_scroll_bg).setVisibility(INVISIBLE); //drag ClipData.Item item = new ClipData.Item("1"); ClipData data = new ClipData("1", new String[]{ClipDescription.MIMETYPE_TEXT_PLAIN}, item); view.startDrag(data, new View.DragShadowBuilder(view), null, 0); //通知adapter变颜色 mSDAdapter.notifyDataSetChanged(); } break; } return true; } //..................... @Override public boolean dispatchTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: if (mIsScrollerScrolling) {//scroll正在滑动的话就不要做其他处理了 return false; } //获取出坐标来 mXDown = (int) ev.getX(); mYDown = (int) ev.getY(); //通过坐标找到在ListView中的位置 mSlideTargetPosition = pointToPosition(mXDown, mYDown); if (mSlideTargetPosition == AdapterView.INVALID_POSITION) { return super.dispatchTouchEvent(ev); } //通过位置找到要slide的view View view = getChildAt(mSlideTargetPosition - getFirstVisiblePosition()); if (view == null) { return super.dispatchTouchEvent(ev); } mSlideTargetView = view.findViewById(R.id.layout_item_scroll); if (mSlideTargetView != null) { //如果已经是滑开了的或者没有滑开的 mXScrollDistance = mSlideTargetView.getScrollX(); } else { mXScrollDistance = 0; } //当前state状态味按下 mState = STATE_DOWN; break; case MotionEvent.ACTION_MOVE: if (mIsScrollerScrolling) {//scroll正在滑动的话就不要做其他处理了 return false; } if (fingerNotMove(ev)) {//手指的范围在50以内 if (mState != STATE_SCROLL && mState != STATE_LONG_CLICK_FINISH && mState != STATE_LONG_CLICK) {//状态不为滑动状态且不为已经触发完成 sendLongClickMessage(); mState = STATE_LONG_CLICK; } else if (mState == STATE_SCROLL) {//当为滑动状态的时候 //有滑动,那么不再触发长点击 removeLongClickMessage(); } } else if (fingerLeftAndRightMove(ev) && mSlideTargetView != null) {//上下范围在50,主要检测左右滑动 boolean bool = false; //这次位置与上一次的不一样,那么要滑这个之前把之前的归位 if (mLastPosition != mSlideTargetPosition) { mLastPosition = mSlideTargetPosition; bool = scrollBack(); } //如果有scroll归位的话的话先跳过这次move if (bool) { return super.dispatchTouchEvent(ev); } //scroll当前的View int moveDistance = (int) ev.getX() - mXDown;//这个往右是正,往左是负 int distance = mXScrollDistance - moveDistance < 0 ? mXScrollDistance - moveDistance : 0; mSlideTargetView.scrollTo(distance, 0); mState = STATE_SCROLL; } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: if (mIsScrollerScrolling) {//scroll正在滑动的话就不要做其他处理了 return false; } if (mSlideTargetView != null && mState == STATE_SCROLL) { //如果滑出的话,那么就滑到固定位置(只要滑出了 mBGWidth / 2 ,就算滑出去了) if (Math.abs(mSlideTargetView.getScrollX()) > mBGWidth / 2) { //通知adapter mSDAdapter.setBtnPosition(mSlideTargetPosition); //不触发onListItemClick事件 mOnListItemClickListener = null; mSDAdapter.setSlideOpenItemPosition(mSlideTargetPosition); if (mOnSlideListener != null) { mOnSlideListener.onSlideOpen(mSlideTargetView, mSlideTargetPosition); } //滑出 int delta = mBGWidth -android ListView显示多个类型item 和 item中控件抢夺焦点解决办法android listview 多个item 倒计时怎么做好
android的listview怎么添加多个格式和布局不一样item,共有4个item?请教大神