仿QQ对话列表滑动删除与置顶的原理及实现(二)

简介: 仿QQ对话列表滑动删除与置顶的原理及实现(二)

4.实现自定义的ListView


如果用系统自己的ListView,将不会将滑动事件发送到SlideView,故需要实现自己的ListView,然而我们并不 是直接继承ListView,而是ListViewCompat,该类也继承自ListView,不过有些扩展的方法可以使用,更加全能。


代码如下:

public class MyListView extends ListViewCompat {
    private SlideView chooseSlideView;
    public MyListView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch(event.getAction()){
            case MotionEvent.ACTION_DOWN:{
                int x=(int)event.getX();
                int y=(int)event.getY();
                int position=pointToPosition(x,y);
                if(position!= ListView.INVALID_POSITION){
                    MessageItem item=(MessageItem)getItemAtPosition(position);
                    this.chooseSlideView=item.mSlideView;
                }
                break;
            }
            default:
                break;
        }
        //将操作提交给SlideView处理
        if(this.chooseSlideView!=null){
            this.chooseSlideView.onRequiredToEvent(event);
        }
        return super.onTouchEvent(event);
    }
}


当接收到触摸事件后,根据X,Y坐标,获得item的ID,提供给我们的方法为pointToPosition(x,y),一看就知道该方法的:point点到Position。每个ListView都有一个无效的位置,比如第一行的前一行,最后一行的后一行,需要作一个判断:position!= ListView.INVALID_POSITION。如果是有效的位置,就获得该item的信息,并设置chooseSlideView。然后将触摸事件提交给item的包装View处理(也就是SlideView)。


MessageItem代码如下:

public class MessageItem {
    public int iconRes;
    public String title;
    public String msg;
    public String time;
    public SlideView mSlideView;
}


5.最后实现Activity代码


启动的Activity需要实现两个接口,一个是SlideView.OnSlideViewOnListener,一个是View.OnClickListener点击事件。


对于ListView需要一个适配器adapter,代码如下:

private class SlideAdapter extends BaseAdapter {
    private LayoutInflater mInflater;
    SlideAdapter() {
        super();
        this.mInflater = getLayoutInflater();
    }
    @Override
    public int getCount() {
        return mMessageItem.size();
    }
    @Override
    public Object getItem(int position) {
        return mMessageItem.get(position);
    }
    @Override
    public long getItemId(int position) {
        return position;
    }
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;
        SlideView slideView = (SlideView) convertView;
        if (slideView == null) {
            View itemView = this.mInflater.inflate(R.layout.list_item, null);
            slideView = new SlideView(MainActivity.this);
            slideView.setContentView(itemView);
            holder = new ViewHolder(slideView);
            slideView.setmOnSlideViewOnListener(MainActivity.this);
            slideView.setTag(holder);
        } else {
            holder = (ViewHolder) slideView.getTag();
        }
        MessageItem item = mMessageItem.get(position);
        item.mSlideView = slideView;
        item.mSlideView.shrink();
        holder.icon.setImageResource(item.iconRes);
        holder.title.setText(item.title);
        holder.msg.setText(item.msg);
        holder.time.setText(item.time);
        holder.delete.setOnClickListener(MainActivity.this);
        holder.top.setOnClickListener(MainActivity.this);
        return slideView;
    }
}


定义LayoutInflater都是用来加载布局文件的,不管是Activity里的setContentView()还是刚才的SlideView的Viiew.inflate其内部实现都是这样做的。


其中list_item代码如下:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <ImageView
        android:id="@+id/icon"
        android:layout_width="50dp"
        android:layout_height="50dp"/>
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBaseline="@+id/icon"
        android:layout_toRightOf="@+id/icon"
        android:orientation="vertical">
        <TextView
            android:id="@+id/title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
        <TextView
            android:id="@+id/msg"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </LinearLayout>
    <TextView
        android:id="@+id/time"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"/>
</RelativeLayout>


这里定义了一个ViewHolder,为的就是防止二次加载。其代码如下:

public class ViewHolder {
    public ImageView icon;
    public TextView title;
    public TextView msg;
    public TextView time;
    public TextView delete;
    public TextView top;
    ViewHolder(View view) {
        this.icon = (ImageView) view.findViewById(R.id.icon);
        this.title = (TextView) view.findViewById(R.id.title);
        this.msg = (TextView) view.findViewById(R.id.msg);
        this.time = (TextView) view.findViewById(R.id.time);
        this.delete = (TextView) view.findViewById(R.id.delete);
        this.top = (TextView) view.findViewById(R.id.top);
    }
}


每次获得MessageItem都调用了item.mSlideView.shrink();将隐藏控件隐藏。其他的代码不用多作解释,ListView基本都是这么用的。


下面我们来看一个接口SlideView.OnSlideViewOnListener实现,代码如下:

@Override
public void Slide(View view, int status) {
    if (this.mLastSlideViewWithStatusOn != null && this.mLastSlideViewWithStatusOn != view) {
        this.mLastSlideViewWithStatusOn.shrink();
    }
    if (status == SlideView.OnSlideViewOnListener.SLIDE_STATUS_ON) {
        this.mLastSlideViewWithStatusOn = (SlideView) view;
    }
}

我们定义了一个记录打开隐藏控件的SlideView:


private SlideView mLastSlideViewWithStatusOn;


当滑动某个控件的时候,就将上一个打开隐藏控件的item关闭。如果这个控件滑动开了,也就记录这个控件status ==SlideView.OnSlideViewOnListener.SLIDE_STATUS_ON:


this.mLastSlideViewWithStatusOn = (SlideView) view;


在来看看实现点击事件的代码:

@Override
public void onClick(View v) {
    switch (v.getId()) {
        case R.id.delete: {
            int position = this.mListView.getPositionForView(v);
            if (position != ListView.INVALID_POSITION) {
                this.mMessageItem.remove(position);
                this.adapter.notifyDataSetChanged();
            }
            break;
        }
        case R.id.top: {
            int position = this.mListView.getPositionForView(v);
            if (position != ListView.INVALID_POSITION) {
                MessageItem item = this.mMessageItem.get(position);
                this.mMessageItem.remove(position);
                this.mMessageItem.add(0, item);
                this.adapter.notifyDataSetChanged();
            }
            break;
        }
        default:
            break;
    }
}


怎么删除item,因为这个代码没有任何解释,有必要说明一下。


为了获取操作屏幕的时值,点击的是那个item的删除按钮,就必须获取他的位置position,我们使用getPositionForView(),position for view,也就是位置根据View获取。接着也是判断获取的position是不是有效的。然后将根据位置删除这个item。mMessageItem:


private List<MessageItem> mMessageItem = new ArrayList<MessageItem>();

是信息的集合。


然后更新ListView:


this.adapter.notifyDataSetChanged();


而置顶思考的思路如下:


获取该item的位置,如上面代码所示,然后获得具体的MessageItem,将集合该位置的MessageItem删除,将具体的MessageItem插入到第一个位置。然后更新ListView如上所示。


置顶后的效果图如下所示:


22.png


下面为初始化Activity:

public void initView() {
    this.mListView = (ListViewCompat) findViewById(R.id.myList);
    for (int i = 0; i < 20; i++) {
        MessageItem item = new MessageItem();
        if (i < 10) {
            item.iconRes =imgRes[i];
            item.title = name[i];
            item.msg = author[i];
            item.time = time[i];
        } else {
            item.iconRes = imgRes[19-i];
            item.title = name[19 - i];
            item.msg = author[19 - i];
            item.time = time[19 - i];
        }
        this.mMessageItem.add(item);
    }
    this.adapter = new SlideAdapter();
    this.mListView.setAdapter(this.adapter);
    this.mListView.setOnItemClickListener(this);
}


成员变更如下:

private String[] name = {"吾国之教育病理", "谷物大脑", "中国历代经济变革得失", "了凡四训", "耶路撒冷三千年", "如果这是宋史", "中国近代史", "罗马人的故事", "光荣与梦想", "大秦帝国"};
private String[] author = {"郑也夫", "戴维·珀尔马特", "吴晓波", "袁了凡", "西蒙·蒙蒂菲奥里", "高天流云 ", "王奇生", "盐野七生 ", "威廉·曼彻斯特 ", "孙皓晖"};
private String[] time = {"2013-10-1 ", " 2015-5-20", "2013-8-1 ", "2007", "2013年3月", "2008", "2013年7月22日", "2011 年12月", "2006年", "2012年5月"};
private int[] imgRes = {R.drawable.book1, R.drawable.book2, R.drawable.book3, R.drawable.book4, R.drawable.book5, R.drawable.book6, R.drawable.book7, R.drawable.book8, R.drawable.book9, R.drawable.book10};
private ListViewCompat mListView;
private List<MessageItem> mMessageItem = new ArrayList<MessageItem>();
private SlideAdapter adapter;
// 上次处于打开状态的SlideView
private SlideView mLastSlideViewWithStatusOn;


activity_main.xml代码如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <com.example.liyuanjing.slidelistview.MyListView
        android:id="@+id/myList"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
</LinearLayout>


本文章代码下载地址如下:


http://download.csdn.net/detail/liyuanjinglyj/9119875

相关文章
|
12月前
|
小程序 JavaScript
微信小程序向左滑动删除操作(类仿微信、QQ)
微信小程序向左滑动删除操作(类仿微信、QQ)
107 0
|
3月前
Axure 列表左右滑动交互-删除、置顶
Axure 列表左右滑动交互-删除、置顶
190 0
|
4月前
|
小程序
【微信小程序-原生开发】实用教程18 - 九宫格、底部悬停按钮、页内悬浮按钮、拨打电话、一键复制
【微信小程序-原生开发】实用教程18 - 九宫格、底部悬停按钮、页内悬浮按钮、拨打电话、一键复制
57 0
|
XML 前端开发 Android开发
autojs模仿QQ长按弹窗菜单(二)
这个菜单数据应该有哪些属性呢? ​菜单显示的文字 菜单点后的回调函数 因此, 数据大概是这样的
194 0
|
JavaScript Android开发
autojs模仿QQ长按弹窗菜单
我们自顶向下来写代码, 首先我们写的是setTestRecyclerViewAdapter.js, 他这个里面要做几件事: 加载两个类, Adapter和Holder, Holder先加载, 因为他会在Adapter中使用
243 0
|
Android开发 容器
仿QQ对话列表滑动删除与置顶的原理及实现(一)
仿QQ对话列表滑动删除与置顶的原理及实现(一)
138 0
仿QQ对话列表滑动删除与置顶的原理及实现(一)
|
开发工具 Android开发 容器
仿QQ聊天界面文字过长显示
前言 最近一直在做聊天功能,有群聊,有单聊,没有集成第三方SDK(例如环信)。从收到消息推送、插入数据库、到界面显示全是我们自己做的,在这个过程中碰到了很多问题,例如消息同步、前后台切换、界面刷新频率、收到上报等很多细节问题。
812 0