abstract:這篇文章主要介紹了Android開(kāi)發(fā)中模仿qq列表信息滑動(dòng)刪除功能,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下這個(gè)效果的完成主要分為兩個(gè)部分自定義view作為listview的列表項(xiàng) 一個(gè)view里面包括 顯示頭像,名字,消息內(nèi)容等的contentView和滑動(dòng)才能顯示出來(lái)的刪除,置頂?shù)挠疫叢藛蝝enuView 在手指移動(dòng)的時(shí)候同時(shí)改變這兩個(gè)視圖的位置重寫(xiě)listview 判斷item向左還
這篇文章主要介紹了Android開(kāi)發(fā)中模仿qq列表信息滑動(dòng)刪除功能,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
這個(gè)效果的完成主要分為兩個(gè)部分
自定義view作為listview的列表項(xiàng) 一個(gè)view里面包括 顯示頭像,名字,消息內(nèi)容等的contentView和滑動(dòng)才能顯示出來(lái)的刪除,置頂?shù)挠疫叢藛蝝enuView 在手指移動(dòng)的時(shí)候同時(shí)改變這兩個(gè)視圖的位置
重寫(xiě)listview 判斷item向左還是向右滑動(dòng) 正常的滾動(dòng)還是左右滑動(dòng)等等 重寫(xiě)onTouchEvent 進(jìn)行事件分發(fā)
大致思路:
listview進(jìn)行事件分發(fā),判斷需要滑動(dòng)還是滾動(dòng)等狀態(tài),如果需要滑動(dòng)將事件傳遞給item進(jìn)行滑動(dòng)處理. 在item中控制contentView和menuView進(jìn)行位置的變化完成滾動(dòng)效果
重寫(xiě)listview代碼
public class SlideListView extends ListView{ private SlideItem mTouchView=null;//記錄當(dāng)前點(diǎn)擊的item View private float mDownX;//x軸坐標(biāo) private float mDownY;//y軸坐標(biāo) private int mTouchState;//記錄點(diǎn)擊狀態(tài) private int mTouchPosition;//記錄點(diǎn)擊位置 private static final int TOUCH_STATE_NONE=0; //按下?tīng)顟B(tài) private static final int TOUCH_STATE_X=1;//橫滑狀態(tài) private static final int TOUCH_STATE_Y=2;//豎滑狀態(tài) //判斷橫豎滑動(dòng)的最小值 private static final int MAX_Y=5; private static final int MAX_X=3; public SlideListView(Context context) { super(context); } public SlideListView(Context context, AttributeSet attrs) { super(context, attrs); } public SlideListView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override public boolean onTouchEvent(MotionEvent ev) { if (ev.getAction() != MotionEvent.ACTION_DOWN && mTouchView == null) return super.onTouchEvent(ev); switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: //按住的item的position int oldPosition = mTouchPosition; //記錄位置 mDownX = ev.getX(); mDownY = ev.getY(); mTouchState = TOUCH_STATE_NONE; //根據(jù)當(dāng)前橫縱坐標(biāo)點(diǎn)獲取點(diǎn)擊的item的position mTouchPosition = this.pointToPosition((int) ev.getX(), (int) ev.getY()); //判斷當(dāng)前點(diǎn)擊的是否和上次點(diǎn)擊的item是同一個(gè),如果是同一個(gè),并且狀態(tài)是打開(kāi)了的就記錄狀態(tài)和坐標(biāo) //記錄坐標(biāo)通過(guò)Item中的downX屬性 if (mTouchPosition == oldPosition && mTouchView != null && mTouchView.isOpen()) { mTouchState = TOUCH_STATE_X; mTouchView.onSwipe(ev); return true; } //獲取當(dāng)前的item的View View currentView = getChildAt(mTouchPosition - getFirstVisiblePosition()); //如果不是同一個(gè)item 那么點(diǎn)擊的話就關(guān)閉掉之前打開(kāi)的item if (mTouchView != null && mTouchView.isOpen()) { mTouchView.smoothCloseMenu(); mTouchView = null; return super.onTouchEvent(ev); } //判斷該view的類型 if (currentView instanceof SlideItem) { mTouchView = (SlideItem) currentView; } if (mTouchView != null) { mTouchView.onSwipe(ev); } break; case MotionEvent.ACTION_MOVE: float dy = Math.abs((ev.getY() - mDownY)); float dx = Math.abs((ev.getX() - mDownX)); if (mTouchState == TOUCH_STATE_X) { if (mTouchView != null) { //執(zhí)行滑動(dòng) mTouchView.onSwipe(ev); } return true; } else if (mTouchState == TOUCH_STATE_NONE) { //判斷滑動(dòng)方向,x方向執(zhí)行滑動(dòng),Y方向執(zhí)行滾動(dòng) if (Math.abs(dy) > MAX_Y) { mTouchState = TOUCH_STATE_Y; } else if (dx > MAX_X) { mTouchState = TOUCH_STATE_X; } } break; case MotionEvent.ACTION_UP: //判斷狀態(tài) if (mTouchState == TOUCH_STATE_X) { if (mTouchView != null) { mTouchView.onSwipe(ev); //如過(guò)最后狀態(tài)是打開(kāi) 那么就重新初始化 if (!mTouchView.isOpen()) { mTouchPosition = -1; mTouchView = null; } } ev.setAction(MotionEvent.ACTION_CANCEL); super.onTouchEvent(ev); return true; } break; } return super.onTouchEvent(ev); } }
重寫(xiě)item項(xiàng)
view的滑動(dòng)效果都是在里完成的 使用了Scroller類
關(guān)于Scroller的使用文章最后已經(jīng)粘出了大神的帖子 不懂的同學(xué)可以先把Scroller的使用理解了在看這個(gè)滑動(dòng)效果就很好懂了 我在這里簡(jiǎn)單講講
這個(gè)類的并沒(méi)有實(shí)際的完成滾動(dòng)效果 它是一個(gè)計(jì)算控件移動(dòng)軌跡的輔助類,
比如說(shuō):在1秒內(nèi)從位置0移動(dòng)到位置100 這個(gè)類會(huì)計(jì)算出移動(dòng)的數(shù)值,它并沒(méi)有完成滑動(dòng)的效果,但是告訴了我們這個(gè)滑動(dòng)的過(guò)程 實(shí)際的上的view移動(dòng)操作在computeScroll()完成 這個(gè)方法是view的自帶方法 需要我們重寫(xiě)
computeScroll方法又是怎么情況呢 看源碼 本身是個(gè)空的 就等著我們實(shí)現(xiàn) 我們實(shí)際改變view位置的代碼就是在此方法內(nèi)調(diào)用的
額。。。英語(yǔ)一般
大致意思 我們要通過(guò)Scroller實(shí)現(xiàn)一個(gè)滾動(dòng)效果的時(shí)候 父布局就會(huì)調(diào)用此方法來(lái)完成子視圖的位置更新
官方的描述是:當(dāng)我們執(zhí)行ontouch或invalidate()或postInvalidate()都會(huì)導(dǎo)致這個(gè)方法的執(zhí)行
在此方法中不斷的獲取到移動(dòng)的距離 通過(guò)view自帶的layout()方法更新view所在位置
/** * Called by a parent to request that a child UPDATE its values for mScrollX * and mScrollY if necessary. This will typically be done if the child is * animating a scroll using a {@link android.widget.Scroller Scroller} * object. */ public void computeScroll() { } public class SlideItem extends LinearLayout { private View contentView = null; //不滑動(dòng)顯示的view private View menuView = null; //左滑顯示的view //計(jì)算滑動(dòng) 動(dòng)畫(huà)效果 private Scroller mOpenScroller; private Scroller mCloseScroller; private int downX; //開(kāi)始按下的位置 //記錄狀態(tài) private int state = STATE_CLOSE; private static final int STATE_CLOSE = 0; private static final int STATE_OPEN = 1; private int mBaseX;//在關(guān)閉滑動(dòng)的時(shí)候計(jì)算與父布局的剩余距離 public SlideItem(Context context) { super(context); } public SlideItem(Context context, AttributeSet attrs) { super(context, attrs); } public SlideItem(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public void setContentView(View contentView, View rightView){ this.contentView = contentView; this.menuView = rightView; //初始化mColoseScroller和mOpenScroller mCloseScroller=new Scroller(getContext()); mOpenScroller = new Scroller(getContext()); initView(); } //child view的布局參數(shù)設(shè)定好后 添加到parent view里面 private void initView() { //這是設(shè)置寬和高 LayoutParams contentParams = new LayoutParams (LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); LayoutParams rightParams=new LayoutParams (LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); contentView.setLayoutParams(contentParams); contentView.setPadding(10,10,10,10); menuView.setLayoutParams(rightParams); this.addView(contentView); this.addView(menuView); } // 判斷是否滑出的狀態(tài) public boolean isOpen() { return state == STATE_OPEN; } /** * 供listView調(diào)用 進(jìn)行視圖的移動(dòng) listView判斷狀態(tài) 什么情況下左滑 * @param event * @return */ public boolean onSwipe(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: downX = (int) event.getX(); break; case MotionEvent.ACTION_MOVE: //按下位置減去移動(dòng)位置 獲取移動(dòng)的距離 int dis = (int) (downX - event.getX()); if (state == STATE_OPEN) { dis += menuView.getWidth(); } //移動(dòng) move(dis); break; case MotionEvent.ACTION_UP: //當(dāng)滑到右邊視圖一半的距離 自動(dòng)滑進(jìn)滑出 if ((downX - event.getX()) > (menuView.getWidth() / 2)) { smoothOpenMenu(); } else { smoothCloseMenu(); return false; } break; } //消費(fèi)掉事件 return true; } /** * 視圖重新繪制時(shí)調(diào)用 */ @Override public void computeScroll() { if (state == STATE_OPEN) { //computeScrollOffset滑動(dòng)是否結(jié)束 if (mOpenScroller.computeScrollOffset()) { move(mOpenScroller.getCurrX()); postInvalidate(); } } else { if (mCloseScroller.computeScrollOffset()) { move(mBaseX - mCloseScroller.getCurrX()); postInvalidate(); } } } /** * 移動(dòng)視圖 * @param dis */ private void move(int dis) { //這兩個(gè)判斷是為了保證 不要把視圖移動(dòng)過(guò)多 導(dǎo)致視圖偏移 if (dis > menuView.getWidth()) { dis = menuView.getWidth(); } if (dis < 0) { dis = 0; } //view.layout()控制view相對(duì)于其父布局的位置 在觸發(fā)移動(dòng)的時(shí)候調(diào)用不斷改變位置 完成實(shí)際的滑動(dòng)效果 contentView.layout(-dis, contentView.getTop(), contentView.getWidth() - dis, getMeasuredHeight()); menuView.layout(contentView.getWidth() - dis, menuView.getTop(), contentView.getWidth() + menuView.getWidth() - dis, menuView.getBottom()); } /** * 滑動(dòng)關(guān)閉 * contentView.getLeft() 與其父視圖的相對(duì)位置 */ public void smoothCloseMenu() { state = STATE_CLOSE; mBaseX = -contentView.getLeft(); mCloseScroller.startScroll(0, 0, mBaseX, 0, 350); postInvalidate(); } /** * 滑動(dòng)打開(kāi) */ public void smoothOpenMenu() { state = STATE_OPEN; mOpenScroller.startScroll(-contentView.getLeft(), 0, menuView.getWidth(), 0, 350); postInvalidate(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if(menuView != null) menuView.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY)); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { //確保centerView menuView的顯示位置 if(contentView != null) contentView.layout(0, 0, getMeasuredWidth(), contentView.getMeasuredHeight()); if(menuView != null) menuView.layout(getMeasuredWidth(), 0, getMeasuredWidth() + menuView.getMeasuredWidth(), contentView.getMeasuredHeight()); } }
適配器
public class SlideAdapter extends BaseAdapter implements View.OnClickListener{ private List<String> dataList; private Context context; private LayoutInflater inflater; public SlideAdapter(Context context, List<String> dataList) { this.context = context; this.dataList = dataList; this.inflater=LayoutInflater.from(context); } @Override public int getCount() { return 5; } @Override public Object getItem(int position) { return null; } @Override public long getItemId(int position) { return 0; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder=null; if (convertView==null){ View content=inflater.inflate(R.layout.adapter_item_content,null); View menu=inflater.inflate(R.layout.adapter_item_menu,null); holder=new ViewHolder(content,menu); SlideItem slideItem=new SlideItem(context); slideItem.setContentView(content,menu); convertView=slideItem; convertView.setTag(holder); }else { holder= (ViewHolder) convertView.getTag(); } holder.itemTvDelete.setOnClickListener(this); holder.itemTvNoRead.setOnClickListener(this); holder.itemTvToTop.setOnClickListener(this); return convertView; } class ViewHolder{ TextView itemTvToTop; TextView itemTvNoRead; TextView itemTvDelete; public ViewHolder(View center,View menu) { this.itemTvToTop = (TextView) menu.findViewById(R.id.item_to_top); this.itemTvNoRead = (TextView) menu.findViewById(R.id.item_no_read); this.itemTvDelete = (TextView) menu.findViewById(R.id.item_DELETE); } } @Override public void onClick(View v) { switch (v.getId()){ case R.id.item_no_read: Toast.makeText(context,"標(biāo)為未讀",Toast.LENGTH_SHORT).show(); break; case R.id.item_to_top: Toast.makeText(context,"置頂了熬",Toast.LENGTH_SHORT).show(); break; case R.id.item_DELETE: Toast.makeText(context,"刪除啦",Toast.LENGTH_SHORT).show(); break; } } }
更多關(guān)于Android開(kāi)發(fā)中模仿qq列表信息滑動(dòng)刪除功能請(qǐng)關(guān)注PHP中文網(wǎng)(ipnx.cn)其他文章!