仿QQ主界面滑动删除置顶

主界面的布局:

image

向左滑动之后出现“删除”和“置顶”两个按钮。

image

再点击列表项一下后会返回到原来的状态。

思路:

        使用scroller类实现一个自定义View,并将自定义View作为ListView的子项布局呈现出来,功能在自定义View中实现。

代码实现:

        先实现自定义View。 自定义View含有三个部分,1个TextView,2个Button,两个Button覆盖在TextView上方,所以让自定义View继承自RelativeLayout比较合适。

public class SlideMenuView extends RelativeLayout {

    private int mScreenWidth;
    private int mScreenHeight;
    private Context mContext;
    private int mDeleteWidth;
    private int mDeleteHeight;
    private int state = 0;
    private static final int START = 0;
    private static final int PULL = 1;
    private static final int RELEASE = 2;
    private int mStartX;
    private int mStartY;
    private int mTotalMoveX;
    private Scroller mScroller;
    private boolean isShowing = false;
    private OnClickListener mOnDeleteClickListener;
    private OnClickListener mOnTopClickListener;
    private String name;
    private TextView mTvName;
    private Button mBtnDelete;
    private Button mBtnTop;
    private int start_x;
    private int start_y;

    public SlideMenuView(Context context) {
        super(context);
        init(context);
    }

    public SlideMenuView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public SlideMenuView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    private void init(Context mContext) {

        DisplayMetrics metrics = mContext.getResources().getDisplayMetrics();
        mScreenWidth = metrics.widthPixels;
        mScreenHeight = metrics.heightPixels;
        mScroller = new Scroller(mContext);
        this.mContext = mContext;
    }

    public void setOnDeleteClickListener(OnClickListener listener) {
        this.mOnDeleteClickListener = listener;
    }

    public void setOnTopClickListener(OnClickListener listener) {
        this.mOnTopClickListener = listener;
    }

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

    //由于是自定义LinearLayout,应该覆盖这个方法来实现自己对控件宽高的安排
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //if语句用来判断子控件的数量,也可以通过getChildAt来获取子控件
        if (getChildCount() > 0) {
            for (int n = 0; n < getChildCount(); n++) {
                View view = getChildAt(n);
                measureChild(view, widthMeasureSpec, heightMeasureSpec);
            }
        }
    }
    //由于布局文件中包含有三个子控件,在自定义View中不要使用findViewById来寻找子控件。
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        if (getChildCount() > 0) {
            for (int n = 0; n < getChildCount(); n++) {
                View view = getChildAt(n);
                if (n == 0) {
                    view.layout(l, t, view.getMeasuredWidth(), b);
                } else if (n == 1) {
                    mDeleteHeight = view.getMeasuredHeight();
                    mDeleteWidth = view.getMeasuredWidth();
                    view.layout(mScreenWidth, t, mScreenWidth + view.getMeasuredWidth(), b);
                } else if (n == 2) {
                    view.layout(mScreenWidth + view.getMeasuredWidth(), t, mScreenWidth + view.getMeasuredWidth() * 2, b);
                }
            }
        }
        //跟布局代码相关
        mTvName = (TextView) getChildAt(0);
        mBtnDelete = (Button) getChildAt(1);
        mBtnTop = (Button) getChildAt(2);
        if (mOnDeleteClickListener != null) {
            mBtnDelete.setOnClickListener(mOnDeleteClickListener);
        }
        if (mOnTopClickListener != null) {
            mBtnTop.setOnClickListener(mOnTopClickListener);
        }
        if (name != null) {
            mTvName.setText(name);
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (getChildCount() > 0) {
            for (int n = 0; n < getChildCount(); n++) {
                View view = getChildAt(n);
                drawChild(canvas, view, 50);
            }
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {

        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN://记录坐标点
                mStartX = (int) ev.getX();
                mStartY = (int) ev.getY();
                start_x = (int) ev.getX();
                start_y = (int) ev.getY();
                state = PULL;
                break;
            case MotionEvent.ACTION_MOVE:
                //不让listView拦截事件
                if (Math.abs(ev.getX() - start_x) > Math.abs(ev.getY() - start_y)) {
                    requestDisallowInterceptTouchEvent(true);//不让父控件去处理这个方法
                } else {
                    requestDisallowInterceptTouchEvent(false);
                }
                if (state == PULL) {
                    mTotalMoveX += ((ev.getX() - mStartX));
                    if (!isShowing && mTotalMoveX < 0) {
                        scrollBy((int) (mStartX - ev.getX()), 0);
                    }
                    mStartX = (int) ev.getX();
                }
                break;
            case MotionEvent.ACTION_UP://在抬手的时候再实现滑动操作
                if (mTotalMoveX < 0) {
                    if (!isShowing) {
                        if (Math.abs(mTotalMoveX) > mDeleteWidth * 2 / 3) {
                            startScroll(-mTotalMoveX, 0, mDeleteWidth * 2 + mTotalMoveX, 0, 500);
                            isShowing = true;
                        } else {
                            startScroll(-mTotalMoveX, 0, mTotalMoveX, 0, 500);
                            isShowing = false;
                        }
                    }
                } else {
                    if (isShowing) {
                        startScroll(mDeleteWidth * 2, 0, -mDeleteWidth * 2, 0, 500);
                        isShowing = false;
                    }
                }
                mTotalMoveX = 0;
                state = START;
                break;
        }
        return true;
    }

    public void startScroll(int startX, int startY, int dx, int dy, int time) {

        mScroller.startScroll(startX, startY, dx, dy, time);
        postInvalidate();//调用computeScroll()函数
    }

    /**
     * 完成实际的滚动
     */
    @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {//如果滑动操作还没有完成,则会返回true
            scrollTo(mScroller.getCurrX(), 0);
            invalidate();
        }
    }
}

然后再MainActivity中给ListView填充自定义View。

public class MainActivity extends Activity {

    private ListView mListView;
    private MyAdapter mAdapter;
    private ArrayList<String> list;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initList();
        mListView = (ListView) findViewById(R.id.listView);

        mAdapter = new MyAdapter(this, list);

        mListView.setAdapter(mAdapter);
    }

    public void initList() {

        list = new ArrayList<>();
        list.add("测试1");
        list.add("测试2");
        list.add("测试3");
        list.add("测试4");
        list.add("测试5");
        list.add("测试6");
        list.add("测试7");
        list.add("测试8");
        list.add("测试9");
        list.add("测试10");
    }

    private class MyAdapter extends BaseAdapter {

        private Context mContext;

        private ArrayList<String> list;

        private LayoutInflater mInflater;

        public MyAdapter(Context mContext, ArrayList<String> list) {

            this.mContext = mContext;
            this.list = list;
            mInflater = LayoutInflater.from(mContext);
        }

        @Override
        public int getCount() {
            if (list != null && list.size() > 0) {
                return list.size();
            }
            return 0;
        }

        @Override
        public Object getItem(int position) {
            return list.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {

            Holder holder;
            if (convertView == null) {
                holder = new Holder();
                convertView = mInflater.inflate(R.layout.item_test, null);
                holder.view = (SlideMenuView) convertView.findViewById(R.id.view);
                convertView.setTag(holder);
            } else {
                holder = (Holder) convertView.getTag();
                holder.view.setTextName("");
            }

            String item = (String) getItem(position);

            if (item != null && !"".equals(item)) {
                holder.view.setTextName(item);
            }
            return convertView;
        }

        /**
         * Holder模式要解决的是性能问题:

         场景:在我们的Adapter中每次都要从row中通过findViewById来找到子控件,然后设置值。
         如果row的布局比较复杂,或者row的数目特别多。这个查找就要不断发生。从而导致性能问题。

         方案:在row第一次被构建出来的时候,调用findViewById, 通过Holder对象存储起来,
         然后把Holder对象通过row.setTag方法,直接缓存在row上。这样下次就不用在查找了。
         */
        public class Holder {

            private SlideMenuView view;
        }
    }
}

最后布局文件(2个):

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <ListView
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:divider="@color/line"
        android:dividerHeight="1dp"
        android:cacheColorHint="@android:color/transparent">

    </ListView>

</RelativeLayout>
<?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">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="40dp">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="40dp"
            android:text="大神在此"
            android:textSize="16dp"
            android:gravity="center_vertical"
            />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="40dp"
            android:text="删除"
            android:textSize="16dp"
            android:background="@android:color/transparent"
            />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="40dp"
            android:text="置顶"
            android:textSize="16dp"
            android:background="@android:color/transparent"
            />

    </RelativeLayout>

</RelativeLayout>

完成。

参考:http://ipjmc.iteye.com/blog/1615828

http://blog.csdn.net/jdsjlzx/article/details/7978860

posted @ 2015-10-10 11:02  黑泡man  阅读(637)  评论(0编辑  收藏  举报