AndroidNote icon indicating copy to clipboard operation
AndroidNote copied to clipboard

示例代码_CheckView

Open GcsSloop opened this issue 9 years ago • 31 comments

package com.sloop.canvas;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

/**
 * <ul type="disc">
 * <li>Author: Sloop</li>
 * <li>Version: v1.0.0</li>
 * <li>Copyright: Copyright (c) 2015</li>
 * <li>Date: 2016/2/5</li>
 * <li><a href="http://www.sloop.icoc.cc"    target="_blank">作者网站</a>      </li>
 * <li><a href="http://weibo.com/5459430586" target="_blank">作者微博</a>      </li>
 * <li><a href="https://github.com/GcsSloop" target="_blank">作者GitHub</a>   </li>
 * </ul>
 */
public class CheckView extends View {

    private static final int ANIM_NULL = 0;         //动画状态-没有
    private static final int ANIM_CHECK = 1;        //动画状态-开启
    private static final int ANIM_UNCHECK = 2;      //动画状态-结束

    private Context mContext;           // 上下文
    private int mWidth, mHeight;        // 宽高
    private Handler mHandler;           // handler

    private Paint mPaint;
    private Bitmap okBitmap;

    private int animCurrentPage = -1;       // 当前页码
    private int animMaxPage = 13;           // 总页数
    private int animDuration = 500;         // 动画时长
    private int animState = ANIM_NULL;      // 动画状态

    private boolean isCheck = false;        // 是否只选中状态

    public CheckView(Context context) {
        super(context, null);

    }

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

    /**
     * 初始化
     * @param context
     */
    private void init(Context context) {
        mContext = context;

        mPaint = new Paint();
        mPaint.setColor(0xffFF5317);
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setAntiAlias(true);

        okBitmap = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.checkres);

        mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                if (animCurrentPage < animMaxPage && animCurrentPage >= 0) {
                    invalidate();
                    if (animState == ANIM_NULL)
                        return;
                    if (animState == ANIM_CHECK) {

                        animCurrentPage++;
                    } else if (animState == ANIM_UNCHECK) {
                        animCurrentPage--;
                    }

                    this.sendEmptyMessageDelayed(0, animDuration / animMaxPage);
                    Log.e("AAA", "Count=" + animCurrentPage);
                } else {
                    if (isCheck) {
                        animCurrentPage = animMaxPage - 1;
                    } else {
                        animCurrentPage = -1;
                    }
                    invalidate();
                    animState = ANIM_NULL;
                }
            }
        };
    }


    /**
     * View大小确定
     * @param w
     * @param h
     * @param oldw
     * @param oldh
     */
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mWidth = w;
        mHeight = h;
    }

    /**
     * 绘制内容
     * @param canvas
     */
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        // 移动坐标系到画布中央
        canvas.translate(mWidth / 2, mHeight / 2);

        // 绘制背景圆形
        canvas.drawCircle(0, 0, 240, mPaint);

        // 得出图像边长
        int sideLength = okBitmap.getHeight();

        // 得到图像选区 和 实际绘制位置
        Rect src = new Rect(sideLength * animCurrentPage, 0, sideLength * (animCurrentPage + 1), sideLength);
        Rect dst = new Rect(-200, -200, 200, 200);

        // 绘制
        canvas.drawBitmap(okBitmap, src, dst, null);
    }


    /**
     * 选择
     */
    public void check() {
        if (animState != ANIM_NULL || isCheck)
            return;
        animState = ANIM_CHECK;
        animCurrentPage = 0;
        mHandler.sendEmptyMessageDelayed(0, animDuration / animMaxPage);
        isCheck = true;
    }

    /**
     * 取消选择
     */
    public void unCheck() {
        if (animState != ANIM_NULL || (!isCheck))
            return;
        animState = ANIM_UNCHECK;
        animCurrentPage = animMaxPage - 1;
        mHandler.sendEmptyMessageDelayed(0, animDuration / animMaxPage);
        isCheck = false;
    }

    /**
     * 设置动画时长
     * @param animDuration
     */
    public void setAnimDuration(int animDuration) {
        if (animDuration <= 0)
            return;
        this.animDuration = animDuration;
    }

    /**
     * 设置背景圆形颜色
     * @param color
     */
    public void setBackgroundColor(int color){
        mPaint.setColor(color);
    }
}

GcsSloop avatar Feb 05 '16 16:02 GcsSloop

 public void handleMessage(Message msg)

中,应该在

  this.sendEmptyMessageDelayed(0, animDuration / animMaxPage);

前面或者后面invalidate();,不然是看不到动画的,每更新一张picture都刷新一下。

hunao0221 avatar Jun 11 '16 00:06 hunao0221

挺好

bqq1986 avatar Jun 21 '16 09:06 bqq1986

赞一个

wonbin2011 avatar Jun 22 '16 15:06 wonbin2011

感谢分享!

flyzend avatar Sep 07 '16 01:09 flyzend

good

liqiang861220 avatar Oct 19 '16 10:10 liqiang861220

public class CheckView extends View {

private static final int ANIM_NULL = 0;         //动画状态-没有
private static final int ANIM_CHECK = 1;        //动画状态-开启
private static final int ANIM_UNCHECK = 2;      //动画状态-结束

private Context mContext;

private Paint mPaint;
private int mWidth, mHeight;//宽高
private Handler handler;
private Bitmap okBitmap;

private int currentPage = -1;//当前页数
private int animCurrentPage = -1;       // 当前页码
private int animMaxPage = 13;           // 总页数
private int animDuration = 500;         // 动画时长

private int animState = ANIM_NULL;      // 动画状态

private boolean isCheck = false;        // 是否只选中状态

public CheckView(Context context) {
    this(context, null);
}

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

/**
 * 初始化
 *
 * @param context
 */
private void init(Context context) {
    mContext = context;

    mPaint = new Paint();
    mPaint.setColor(0xffFF5317);
    mPaint.setStyle(Paint.Style.FILL);
    mPaint.setAntiAlias(true);

    okBitmap = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.checkmark);

    handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (animCurrentPage < animMaxPage && animCurrentPage >= 0) {
                invalidate();
                if (animState == ANIM_NULL)
                    return;
                if (animState == ANIM_CHECK) {

                    animCurrentPage++;
                } else if (animState == ANIM_UNCHECK) {
                    animCurrentPage--;
                }
                invalidate();
                this.sendEmptyMessageDelayed(0, animDuration / animMaxPage);
                Log.e("AAA", "Count=" + animCurrentPage);
            } else {
                if (isCheck) {
                    animCurrentPage = animMaxPage - 1;
                } else {
                    animCurrentPage = -1;
                }
                invalidate();
                animState = ANIM_NULL;
            }
        }
    };
}


/**
 * View大小确定
 *
 * @param w
 * @param h
 * @param oldw
 * @param oldh
 */
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    mWidth = w;
    mHeight = h;
}

/**
 * 绘制内容
 *
 * @param canvas
 */
@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    Log.e("llx", "onDraw()--");
    // 移动坐标系到画布中央
    canvas.translate(mWidth / 2, mHeight / 2);

    // 绘制背景圆形
    canvas.drawCircle(0, 0, 240, mPaint);

    // 得出图像边长
    int sideLength = okBitmap.getHeight();

    // 得到图像选区 和 实际绘制位置
    Rect src = new Rect(sideLength * animCurrentPage, 0, sideLength * (animCurrentPage + 1), sideLength);
    Rect dst = new Rect(200, 200, 200, 200);

    // 绘制
    canvas.drawBitmap(okBitmap, src, dst, null);
}


/**
 * 选择
 */
public void check() {
    if (animState != ANIM_NULL || isCheck)
        return;
    animState = ANIM_CHECK;
    animCurrentPage = 0;
    handler.sendEmptyMessageDelayed(0, animDuration / animMaxPage);
    isCheck = true;
}

/**
 * 取消选择
 */
public void unCheck() {
    if (animState != ANIM_NULL || (!isCheck))
        return;
    animState = ANIM_UNCHECK;
    animCurrentPage = animMaxPage - 1;
    handler.sendEmptyMessageDelayed(0, animDuration / animMaxPage);
    isCheck = false;
}

/**
 * 设置动画时长
 *
 * @param animDuration
 */
public void setAnimDuration(int animDuration) {
    if (animDuration <= 0)
        return;
    this.animDuration = animDuration;
}

/**
 * 设置背景圆形颜色
 *
 * @param color
 */
public void setBackgroundColor(int color) {
    mPaint.setColor(color);
}

}

这是我的代码 为什么图片没有执行显示呢

liulixin1 avatar Oct 21 '16 10:10 liulixin1

注意这里:

Rect dst = new Rect(200, 200, 200, 200);

GcsSloop avatar Oct 21 '16 11:10 GcsSloop

@GcsSloop 是这里的原因 Thanks

liulixin1 avatar Oct 21 '16 11:10 liulixin1

刚看到你的信息,解决了就好。

在 2016-10-21 19:28:46,"Liulixin" [email protected] 写道:

@GcsSloop 是这里的原因 Thanks

— You are receiving this because you commented. Reply to this email directly, view it on GitHub, or mute the thread.

liqiang861220 avatar Oct 25 '16 19:10 liqiang861220

第一个构造方法中使用super在按钮点击事件会报空指针异常,需要改为this(context,null)就不报异常了,但仍无法显示动画啊。从始至终,一直没有动画效果。本人是一个小白,没有办法了。 public CheckView(Context context) { super(context, null); //报异常 }

ghost avatar Nov 19 '16 06:11 ghost

如果你不是直接继承的 View,那么上层View 可能没有两个参数的构造函数,使用 super(context, null); 就可能报错。 另外,想要显示动画效果需要调用 check 活着 unCheck 方法,直接加载 View 是没有效果的。

GcsSloop avatar Nov 19 '16 09:11 GcsSloop

@GcsSloop 我在按钮上已经加入了点击事件了,并且在logCat有打印当前的页码数,只是没有动画效果。所以,我现在也不知道哪里出现了问题。

ghost avatar Nov 19 '16 12:11 ghost

检查你的图片和绘制区域的大小是否正确。

1.先在本应该绘制图片等区域绘制一种和背景不同颜色看是否正确。 2.检查图片是否读取成功。 3.检查图片截取大小是否正确。 4.检查是否是硬件加速的问题。 https://github.com/GcsSloop/AndroidNote/issues/7

GcsSloop avatar Nov 19 '16 13:11 GcsSloop

学习了

newcaoguo avatar Nov 28 '16 07:11 newcaoguo

图片在那能找到?

MLLWF avatar Jan 16 '17 01:01 MLLWF

这篇文章 图片文字 里面

GcsSloop avatar Jan 16 '17 04:01 GcsSloop

你好,有一点不是很明白,当你选定图片的区域绘制到实际的位置的时候图片会自动按照绘制区域的大小进行缩放啊,怎么会达成你说的那种效果呢? @GcsSloop

MLLWF avatar Jan 16 '17 05:01 MLLWF

例如截取的部分是 100 x 100 的大小,但绘制区域是 100 x 200 大小,那么图片高度会放大到原来的两倍,以填充所有的区域。

GcsSloop avatar Jan 16 '17 05:01 GcsSloop

我有个问题,如果一个文字 比如 ‘好’ 女 是绿色 子是红色怎么实现

ZhouKanZ avatar Mar 14 '17 15:03 ZhouKanZ

如果是左右分的,没有交叉部分的文字,就用遮罩,绘制两次,第一次用绿色,区域只包括左边,第二次用红色,区域只包括右边。

GcsSloop avatar Mar 15 '17 08:03 GcsSloop

我按照上面的代码,运行怎么都没有动画效果,在activity中给button设置一个点击时间,调用checkview.check(),但是还是没有动画。

zhouminxia avatar Apr 07 '17 07:04 zhouminxia

@zhouminxia 1.资源文件对吗? 2. 剪裁区域和绘制区域是否修改过? 3. handler 正确执行了吗。

GcsSloop avatar Apr 07 '17 08:04 GcsSloop

为什么我运行的时候报错呢, 说图片资源文件长度太长

BINBINXIAO avatar Jun 13 '17 08:06 BINBINXIAO

换个手机测试一下吧,我也用了 博主的这个 是 完全好用的。你可以看一下。 https://github.com/linsir6/Android-Notes/blob/master/Android%E8%87%AA%E5%AE%9A%E4%B9%89View/%E8%87%AA%E5%AE%9A%E4%B9%89View%E2%80%94%E2%80%94CheckView.md

linsir6 avatar Jun 14 '17 08:06 linsir6

@GcsSloop 用了楼主的代码,发现点击check后最后会闪一下。 调试之后发现是由于调用invalidate()时并不会堵塞主线程,是异步问题导致的。 通过在onDraw方法中打印发现绘制了最大页13之后又绘制了12,所以会闪一下。 这边简单修改了下。只改了handle里面的处理:

if (animCurrentPage < animMaxPage && animCurrentPage >= 0) {
    if (animState == ANIM_NULL)
        return;
    if (animState == ANIM_CHECK) {
        animCurrentPage++;
    } else if (animState == ANIM_UNCHECK) {
        animCurrentPage--;
    }
    //animCurrentPage为animMaxPage不需要更新,否则下面的else会导致绘制前一张图闪一下
    if (animCurrentPage != animMaxPage) {
        invalidate();
    }
    this.sendEmptyMessageDelayed(0, animDuration / animMaxPage);
} else {
    if (isCheck) {
        animCurrentPage = animMaxPage - 1;
    } else {
        animCurrentPage = -1;
    }
    invalidate();
    animState = ANIM_NULL;
}

zhanzengyu avatar Oct 18 '17 02:10 zhanzengyu

xqgdmg avatar Mar 29 '18 09:03 xqgdmg

为啥我的显示效果和你的效果不太一样

huo2016 avatar Jul 04 '18 02:07 huo2016

My Code is as follows,for your reference.(please ignore my shandong chinese english 😔)

public class CheckView extends View { private Paint mPaint; private static final int ANIM_NULL=0; private static final int ANIM_CHECK=1; private static final int ANIM_UNCHECK=2; private int mWidth,mHeight; private Handler mHandler; private Bitmap okBitmap; private int animCurrentPage=-1; //TODO:页面不需要是13页,因该是12页,12页至13页才是正确的绘制区域 private int animMaxPage=12; private int animDuration=500; private int animState=ANIM_NULL; private boolean isCheck=false; private static final String TAG = "RightView";

public CheckView(Context context) {
    super(context);
    init(context,null);
}

public CheckView(Context context, @Nullable AttributeSet attrs) {
    super(context, attrs);
    init(context,attrs);
}
private void init(Context context, @Nullable AttributeSet attrs){
    mPaint=new Paint();
    mPaint.setColor(Color.YELLOW);
    mPaint.setStyle(Paint.Style.FILL);
    mPaint.setAntiAlias(true);
    okBitmap= BitmapFactory.decodeResource(context.getResources(),R.drawable.check_mark);
    mHandler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (animCurrentPage<animMaxPage&&animCurrentPage>=0){
                invalidate();
                if (animState==ANIM_NULL){
                    return;
                }
                if (animState==ANIM_CHECK){
                    animCurrentPage++;
                    Log.e(TAG, "handleMessage: 当前页面"+animCurrentPage );
                }else if (animState==ANIM_UNCHECK){
                    animCurrentPage--;
                }

                this.sendEmptyMessageDelayed(0,animDuration/animMaxPage);
            }else {
                if (isCheck){
                    animCurrentPage=animMaxPage-1;
                }else {
                    animCurrentPage=-1;
                }
                //TODO:这里不需要重绘了,当前页面减一页会导致onDraw()方法多执行一边造成页面回退,出现闪烁现象

// invalidate(); animState=ANIM_NULL; } } };

}

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    mWidth=w;
    mHeight=h;

}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.translate(mWidth/2,mHeight/2);
    canvas.drawCircle(0,0,240,mPaint);
    int sideLength = okBitmap.getHeight();
    // 得到图像选区 和 实际绘制位置
    Rect src = new Rect(sideLength * animCurrentPage, 0, sideLength * (animCurrentPage + 1), sideLength);
    Rect dst = new Rect(-150, -150, 150, 150);
    Log.e(TAG, "onDraw: 绘制区域:"+sideLength * animCurrentPage+"    "+0+"   "+sideLength * (animCurrentPage + 1)+"   "+sideLength );


    // 绘制
    canvas.drawBitmap(okBitmap, src, dst, mPaint);


}

public void check(){
    if (animState!=ANIM_NULL&&isCheck){
        return;
    }else {
        animState=ANIM_CHECK;
        animCurrentPage=0;
        isCheck=true;
        mHandler.sendEmptyMessageDelayed(0,animDuration/animMaxPage);
    }
}

/**
 * 取消选择
 */
public void unCheck() {
    if (animState != ANIM_NULL || (!isCheck))
        return;
    animState = ANIM_UNCHECK;
    animCurrentPage = animMaxPage - 1;
    mHandler.sendEmptyMessageDelayed(0, animDuration / animMaxPage);
    isCheck = false;
}

public void setDuration(int duration){
    if (duration<=0){
        return;
    }else {
        this.animDuration=duration;
    }
}
public void setBackGroundColor(int color){
    mPaint.setColor(color);
}

} 我主要是解决了一下页面闪烁现象,多谢楼主大大 学习了 Master!!!(手动 抱拳O(∩_∩)O )

longjianghong avatar Jul 31 '18 03:07 longjianghong

主要实现功能的代码

 // 指定图片绘制区域
Rect src = new Rect(sideLength * animCurrentPage, 0, sideLength * (animCurrentPage + 1), sideLength);
// 指定图片在屏幕上显示的区域
Rect dst = new Rect(-200, -200, 200, 200);
 // 绘制
canvas.drawBitmap(okBitmap, src, dst, null);

zstartw avatar Aug 21 '18 07:08 zstartw

不知道为什么我照着楼主的onDraw里面做,图片都无法显示,我换成这样,就可以了 override fun onDraw(canvas: Canvas?) { super.onDraw(canvas) if (canvas == null || okBitmap == null) return

    canvas.translate((mWidth / 2).toFloat(), (mHeight / 2).toFloat())

    // 得出图像边长
    val sideLength = okBitmap!!.height

    canvas.drawCircle(0f, 0f, (sideLength / 2 + 20).toFloat(), mPaint)

    // 算出图片每次需要截取的长度
    var right = sideLength / animMaxPage * animCurrentPage
    if (right > sideLength) right = sideLength

    // 算出绘制框需要显示的长度,因为是从中间点左边负的图片边长的一半加上每次变换的值
    var bRight = (0 - sideLength) / 2 + (sideLength / animMaxPage * animCurrentPage)
    if (bRight > sideLength / 2) bRight = sideLength / 2

    // 从图片的顶部坐标0,左边坐标0,底部坐标图片的长度,右边坐标是截取的长度
    val src = Rect(0, 0, right, sideLength)
    // 由于已经移动到屏幕的中间点,所以显示的区域是从中间点开始,顶部的坐标是负的图片边长的一半,底部坐标也是边长的一半
    // 左边坐标也是负的图片边长的一半,右边坐标是上面算出的长度
    val dst = Rect((0 - sideLength) / 2, (0 - sideLength) / 2, bRight, sideLength / 2)

    canvas.drawBitmap(okBitmap, src, dst, null)
}

lodgkk avatar Dec 13 '18 06:12 lodgkk

读取到了Bitmap 也能打印页数,绘制不同颜色的Rect也能出现,但就是没有动画,可以给我一个完整Demo吗?

PaulX1029 avatar Feb 25 '21 08:02 PaulX1029