自定义view实现侧滑删除效果

来源:互联网 发布:linux下部署jira 编辑:程序博客网 时间:2024/05/21 06:51

分享一个最近花了点时间才做出来的效果,先看一下做出来的效果,等以后我学会录制视频了,再传动画效果吧

我直接贴了项目的效果图,难点就是滑动的时候删除按钮的背景和文字变化

我最开始拿到任务的时候,第一想法是百度,然而很难找到同类的效果,基本都是滑动的时候整个item要跟着一起滑动,最后终于在鸿神的博客里面找到一个在item上弹出一个删除按钮的效果,还顺便解除了listview上下滑动和左右滑动的冲突,在这里方式博客地址以示尊重

http://blog.csdn.net/lmj623565791/article/details/22961279

这里我用的自定义listview,在自定义的listview中放上自定义的view,用ondraw的方式去画三种不同状态的按钮,最开始是想图简单,直接用的两张图片去做切换的效果,后来发现图片会有拉伸的问题,处理起来也比较麻烦,所以直接在大牛的指引下开启了自定义view的道路。

下来我们来看看实现的过程,只需要看自定义listview和view就行啦
HistoryListview.java

package com.example.yj;import android.content.Context;import android.util.AttributeSet;import android.view.LayoutInflater;import android.view.MotionEvent;import android.view.View;import android.view.ViewConfiguration;import android.widget.ListView;import com.example.yj.R;public class HistoryListview extends ListView {    private boolean IsSliding; //是否左右滑动    private HistoryDeleteButtonView mDeleteButton;    private LayoutInflater myInflater;    private View mCurrentView; //当前手指触摸的View    private int mCurrentViewPos; //当前手指触摸的位置    private int xDown;//手指按下时的x坐标    private int yDown; //手指按下时的y坐标    private int xMove;//手指移动时的x坐标    private int yMove; //手指移动时的y坐标    private int touchSlop; //用户滑动的最小距离    public HistoryListview(Context context, AttributeSet attrs) {        super(context, attrs);        myInflater = LayoutInflater.from(context);        touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();//移动的最小距离        View view = myInflater.inflate(R.layout.history_list_item, null);        mDeleteButton = (HistoryDeleteButtonView) view.findViewById(R.id.deleteRecord);        mDeleteButton.setOnClickListener(new OnClickListener() {            @Override            public void onClick(View v) {            //根据当前显示的图片是续播还是删除来处理不同的操作,这里还没到实现功能的时候,就先不出来啦                if(mDeleteButton.getmRedBitmapWidth() == 0){                    //续播操作                }else{                    //删除操作                }            }        });    }    @Override    public boolean dispatchTouchEvent(MotionEvent ev) {        int action = ev.getAction();        int x = (int) ev.getX();        int y = (int) ev.getY();        switch (action) {            case MotionEvent.ACTION_DOWN:                xDown = x;                yDown = y;                //按下屏幕的时候改变删除按钮的显示状态                if (mDeleteButton.getmRedBitmapWidth()>0) {                    mDeleteButton.setmRedBitmapWidth(0);                    mDeleteButton.postInvalidate();                    return false;                }                // 获得当前手指按下时的item的位置                mCurrentViewPos = pointToPosition(xDown, yDown);                // 获得当前手指按下时的item                View view = getChildAt(mCurrentViewPos - getFirstVisiblePosition());                mCurrentView = view;                break;            case MotionEvent.ACTION_MOVE:                xMove = x;                yMove = y;                int dx = xMove - xDown;                int dy = yMove - yDown;                //判断是否是从右到左的滑动                if (xMove < xDown && Math.abs(dx) > touchSlop && Math.abs(dy) < touchSlop) {                    IsSliding = true;                }                break;        }        return super.dispatchTouchEvent(ev);    }    @Override    public boolean onTouchEvent(MotionEvent ev) {        int action = ev.getAction();        //从左到右滑动才显示删除按钮        if (IsSliding) {            switch (action) {                case MotionEvent.ACTION_MOVE:                    xMove = (int) ev.getX();                    int[] location = new int[2];                    // 获得当前item的位置x与y                    mCurrentView.getLocationOnScreen(location);                    mDeleteButton = (HistoryDeleteButtonView) mCurrentView.findViewById(R.id.deleteRecord);                    int moveWid = (xDown - xMove) / 2;                    if (moveWid < 0) {                        return false;                    }                    if (moveWid <= mDeleteButton.getmViewWidth()) {                        mDeleteButton.setmRedBitmapWidth(moveWid);                        mDeleteButton.postInvalidate();                    } else {                        moveWid = mDeleteButton.getmViewWidth();                        mDeleteButton.setmRedBitmapWidth(moveWid);                        mDeleteButton.postInvalidate();                    }                    break;                case MotionEvent.ACTION_UP:                    xMove = (int) ev.getX();                    moveWid = (xDown - xMove) / 2; //item上滑动两个像素,button上改变一个像素                    if (moveWid< mDeleteButton.getmViewWidth()) {                        mDeleteButton.setmRedBitmapWidth(0);                        mDeleteButton.postInvalidate();                    }                    IsSliding = false;            }            return true;        }        return super.onTouchEvent(ev);    }}

这里比较重要的就是要解决侧滑冲突就必须要重写listview的dispatchTouchEvent方法,当左右滑动的距离大于上下滑动的距离时才把事件交给onTouchEvent去处理,然后在dispatchTouchEvent的down事件里面去切换删除按钮和续播按钮的显示,这样才不会出现,滑动完一个,再去滑下一个,然后两个按钮跟着一起动的奇怪场景了

然后在onTouchEvent中的move事件里面去获取当前手指侧滑的距离,再传给我们的自定义view,告诉自定义view当前红色背景需要绘制的宽度

然后是比较重要的画button的地方啦,这个自定义控件我是直接放在history_list_item里面的,但是我在adapter里面并没有对他进行任何操作。

HistoryDeleteButtonView.java

package com.example.yj;import android.content.Context;import android.graphics.Bitmap;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.PorterDuff;import android.graphics.RectF;import android.util.AttributeSet;import android.view.View;import com.example.yj.R;public class HistoryDeleteButtonView extends View {    private static int TEXT_SIZE = 36;//文字大小    private static int BORDER_WIDTH = 2;//边框线宽度    private static int REDUIS = 12;//圆角半径    private static String TEXT = "删除";    private static String RESUME_PALY ="续播";    private Paint mPaint;    private RectF mViewRect;//控件显示的矩形区域    //控件的宽高    private int mViewHeight;    private int mViewWidth;    private int mRedBitmapWidth;//红色背景区域的宽度    private Bitmap redBackgroundBitmap;    private Bitmap whiteBackgroundBitmap;    private Bitmap resumepalyBitmap;    public HistoryDeleteButtonView(Context context, AttributeSet attrs) {        super(context, attrs);        //初始化画笔        mPaint = new Paint();        mPaint.setTextAlign(Paint.Align.CENTER);        mPaint.setTextSize(TEXT_SIZE);        mPaint.setAntiAlias(true);    }    @Override    protected void onDraw(Canvas canvas) {        //获取红色背景的宽度        mRedBitmapWidth = getmRedBitmapWidth();        if (mRedBitmapWidth == 0) {            canvas.drawBitmap(resumepalyBitmap, 0, 0, null);        } else if (mRedBitmapWidth < mViewWidth) {            //清空一下画布内容,避免重叠            canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);            invalidate();            canvas.drawBitmap(whiteBackgroundBitmap, 0, 0, null);            canvas.clipRect(mViewWidth-mRedBitmapWidth,0,mViewWidth,mViewHeight);            canvas.drawBitmap(redBackgroundBitmap, 0, 0, null);        } else {            canvas.drawBitmap(redBackgroundBitmap, 0, 0, null);        }    }    @Override    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {        super.onLayout(changed, left, top, right, bottom);        if (changed) {            mViewHeight = getHeight();            mViewWidth = getWidth();            setmViewWidth(mViewWidth);            initBitmaps();        }    }    //创建三种不同状态下的bitmap(续播,删除)    private void initBitmaps() {        //设置矩形区域大小(减去了边框的宽度)        mViewRect = new RectF(BORDER_WIDTH, BORDER_WIDTH, mViewWidth - BORDER_WIDTH, mViewHeight - BORDER_WIDTH);        Paint.FontMetricsInt fontMetrics = mPaint.getFontMetricsInt();        //让文字垂直居中的参数        float baseline = (mViewRect.bottom + mViewRect.top - fontMetrics.bottom - fontMetrics.top) / 2;        //创建两个bitmap        whiteBackgroundBitmap = Bitmap.createBitmap(mViewWidth, mViewHeight, Bitmap.Config.ARGB_8888);        redBackgroundBitmap = Bitmap.createBitmap(mViewWidth, mViewHeight, Bitmap.Config.ARGB_8888);        resumepalyBitmap = Bitmap.createBitmap(mViewWidth, mViewHeight, Bitmap.Config.ARGB_8888);        Canvas whiteCanvas = new Canvas(whiteBackgroundBitmap);        Canvas redCanvas = new Canvas(redBackgroundBitmap);        Canvas resumeplayCanvas = new Canvas(resumepalyBitmap);        //给画布设置好背景颜色        whiteCanvas.drawColor(getResources().getColor(R.color.history_delete_white));        redCanvas.drawColor(getResources().getColor(R.color.history_delete_white));        resumeplayCanvas.drawColor(getResources().getColor(R.color.history_delete_white));        //用空心线画红色边框        mPaint.setStyle(Paint.Style.STROKE);        mPaint.setStrokeWidth(BORDER_WIDTH);        mPaint.setColor(getResources().getColor(R.color.history_delete_red));        whiteCanvas.drawRoundRect(mViewRect, REDUIS, REDUIS, mPaint);        redCanvas.drawRoundRect(mViewRect, REDUIS, REDUIS, mPaint);        resumeplayCanvas.drawRoundRect(mViewRect, REDUIS, REDUIS, mPaint);        //用实心线画红色文字和红色背景        mPaint.setStyle(Paint.Style.FILL);        mPaint.setColor(getResources().getColor(R.color.history_delete_red));        whiteCanvas.drawText(TEXT, mViewRect.centerX(), baseline, mPaint);        redCanvas.drawRoundRect(mViewRect, REDUIS, REDUIS, mPaint);        resumeplayCanvas.drawText(RESUME_PALY, mViewRect.centerX(), baseline, mPaint);        //用实心线画白色文字        mPaint.setColor(getResources().getColor(R.color.history_delete_white));        redCanvas.drawText(TEXT, mViewRect.centerX(), baseline, mPaint);    }    public int getmRedBitmapWidth() {        return mRedBitmapWidth;    }    public void setmRedBitmapWidth(int mRedBitmapWidth) {        this.mRedBitmapWidth = mRedBitmapWidth;    }    public int getmViewWidth() {        return mViewWidth;    }    public void setmViewWidth(int mViewWidth) {        this.mViewWidth = mViewWidth;    }}

我是先采取在在onlayout里面获取到控件宽高的同时去创建好三张不同状态的bitmap,然后再在ondraw里面根据在listview中获取的滑动距离去选择画哪个bitmap

最麻烦的就是需要白色背景和红色背景切换的时候,最开始是先去把bitmap截取好,然后在用canvas把两张bitmap合并起来,大牛只看了一眼就说这样不好,然后就换成clipRect了,然后我就去学了它的用法,最后总算是优化了一点点。

就是这两个啦 ,画图的过程自己觉得还是处理的比较繁琐的,如果有优化的办法,希望我们可以交流一下。

1 0
原创粉丝点击