Welcome

首页 / 移动开发 / Android / Android开发中模仿qq列表信息滑动删除功能


这个效果的完成主要分为两个部分
自定义view作为listview的列表项 一个view里面包括 显示头像,名字,消息内容等的contentView和滑动才能显示出来的删除,置顶的右边菜单menuView 在手指移动的时候同时改变这两个视图的位置
重写listview 判断item向左还是向右滑动 正常的滚动还是左右滑动等等 重写onTouchEvent 进行事件分发
大致思路:
listview进行事件分发,判断需要滑动还是滚动等状态,如果需要滑动将事件传递给item进行滑动处理. 在item中控制contentView和menuView进行位置的变化完成滚动效果
重写listview代码

public class SlideListView extends ListView{private SlideItem mTouchView=null;//记录当前点击的item Viewprivate float mDownX;//x轴坐标private float mDownY;//y轴坐标private int mTouchState;//记录点击状态private int mTouchPosition;//记录点击位置private static final int TOUCH_STATE_NONE=0; //按下状态private static final int TOUCH_STATE_X=1;//横滑状态private static final int TOUCH_STATE_Y=2;//竖滑状态//判断横竖滑动的最小值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);}@Overridepublic boolean onTouchEvent(MotionEvent ev) {if (ev.getAction() != MotionEvent.ACTION_DOWN && mTouchView == null)return super.onTouchEvent(ev);switch (ev.getAction()) {case MotionEvent.ACTION_DOWN://按住的item的positionint oldPosition = mTouchPosition;//记录位置mDownX = ev.getX();mDownY = ev.getY();mTouchState = TOUCH_STATE_NONE;//根据当前横纵坐标点获取点击的item的positionmTouchPosition = this.pointToPosition((int) ev.getX(), (int) ev.getY());//判断当前点击的是否和上次点击的item是同一个,如果是同一个,并且状态是打开了的就记录状态和坐标//记录坐标通过Item中的downX属性if (mTouchPosition == oldPosition && mTouchView != null && mTouchView.isOpen()) {mTouchState = TOUCH_STATE_X;mTouchView.onSwipe(ev);return true;}//获取当前的item的ViewView currentView = getChildAt(mTouchPosition - getFirstVisiblePosition());//如果不是同一个item 那么点击的话就关闭掉之前打开的itemif (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) {//执行滑动mTouchView.onSwipe(ev);}return true;} else if (mTouchState == TOUCH_STATE_NONE) {//判断滑动方向,x方向执行滑动,Y方向执行滚动if (Math.abs(dy) > MAX_Y) {mTouchState = TOUCH_STATE_Y;} else if (dx > MAX_X) {mTouchState = TOUCH_STATE_X;}}break;case MotionEvent.ACTION_UP://判断状态if (mTouchState == TOUCH_STATE_X) {if (mTouchView != null) {mTouchView.onSwipe(ev);//如过最后状态是打开 那么就重新初始化if (!mTouchView.isOpen()) {mTouchPosition = -1;mTouchView = null;}}ev.setAction(MotionEvent.ACTION_CANCEL);super.onTouchEvent(ev);return true;}break;}return super.onTouchEvent(ev);}}
重写item项
view的滑动效果都是在里完成的 使用了Scroller类
关于Scroller的使用文章最后已经粘出了大神的帖子 不懂的同学可以先把Scroller的使用理解了在看这个滑动效果就很好懂了 我在这里简单讲讲
这个类的并没有实际的完成滚动效果 它是一个计算控件移动轨迹的辅助类,
比如说:在1秒内从位置0移动到位置100 这个类会计算出移动的数值,它并没有完成滑动的效果,但是告诉了我们这个滑动的过程 实际的上的view移动操作在computeScroll()完成 这个方法是view的自带方法 需要我们重写
computeScroll方法又是怎么情况呢 看源码 本身是个空的 就等着我们实现 我们实际改变view位置的代码就是在此方法内调用的
额。。。英语一般
大致意思 我们要通过Scroller实现一个滚动效果的时候 父布局就会调用此方法来完成子视图的位置更新
官方的描述是:当我们执行ontouch或invalidate()或postInvalidate()都会导致这个方法的执行
在此方法中不断的获取到移动的距离 通过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; //不滑动显示的viewprivate View menuView = null; //左滑显示的view//计算滑动 动画效果private Scroller mOpenScroller;private Scroller mCloseScroller;private int downX; //开始按下的位置//记录状态private int state = STATE_CLOSE;private static final int STATE_CLOSE = 0;private static final int STATE_OPEN = 1;private int mBaseX;//在关闭滑动的时候计算与父布局的剩余距离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和mOpenScrollermCloseScroller=new Scroller(getContext());mOpenScroller = new Scroller(getContext());initView();}//child view的布局参数设定好后 添加到parent view里面private void initView() {//这是设置宽和高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);}// 判断是否滑出的状态public boolean isOpen() {return state == STATE_OPEN;}/** * 供listView调用 进行视图的移动listView判断状态 什么情况下左滑 * @param event * @return */public boolean onSwipe(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:downX = (int) event.getX();break;case MotionEvent.ACTION_MOVE://按下位置减去移动位置 获取移动的距离int dis = (int) (downX - event.getX());if (state == STATE_OPEN) {dis += menuView.getWidth();}//移动move(dis);break;case MotionEvent.ACTION_UP://当滑到右边视图一半的距离 自动滑进滑出if ((downX - event.getX()) > (menuView.getWidth() / 2)) {smoothOpenMenu();} else {smoothCloseMenu();return false;}break;}//消费掉事件return true;}/** * 视图重新绘制时调用 */@Overridepublic void computeScroll() {if (state == STATE_OPEN) {//computeScrollOffset滑动是否结束if (mOpenScroller.computeScrollOffset()) {move(mOpenScroller.getCurrX());postInvalidate();}} else {if (mCloseScroller.computeScrollOffset()) {move(mBaseX - mCloseScroller.getCurrX());postInvalidate();}}}/** * 移动视图 * @param dis */private void move(int dis) {//这两个判断是为了保证 不要把视图移动过多 导致视图偏移if (dis > menuView.getWidth()) {dis = menuView.getWidth();}if (dis < 0) {dis = 0;}//view.layout()控制view相对于其父布局的位置在触发移动的时候调用不断改变位置 完成实际的滑动效果contentView.layout(-dis, contentView.getTop(), contentView.getWidth() - dis, getMeasuredHeight());menuView.layout(contentView.getWidth() - dis, menuView.getTop(), contentView.getWidth() + menuView.getWidth() - dis, menuView.getBottom());}/** * 滑动关闭 * contentView.getLeft() 与其父视图的相对位置 */public void smoothCloseMenu() {state = STATE_CLOSE;mBaseX = -contentView.getLeft();mCloseScroller.startScroll(0, 0, mBaseX, 0, 350);postInvalidate();}/** * 滑动打开 */public void smoothOpenMenu() {state = STATE_OPEN;mOpenScroller.startScroll(-contentView.getLeft(), 0, menuView.getWidth(), 0, 350);postInvalidate();}@Overrideprotected 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));}@Overrideprotected 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);}@Overridepublic int getCount() {return 5;}@Overridepublic Object getItem(int position) {return null;}@Overridepublic long getItemId(int position) {return 0;}@Overridepublic 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);}}@Overridepublic void onClick(View v) {switch (v.getId()){case R.id.item_no_read:Toast.makeText(context,"标为未读",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;}}}
参考文档:
SwipeMenuListView github上的实现此效果的开源项目
以上所述是小编给大家介绍的Android开发中模仿qq列表信息滑动删除功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对脚本之家网站的支持!