自定义View(二)、Canvas和Paint的用法、BitMap

自定义View

首先我们接着上一篇博客,再写两个自定义View的应用,一个类似电池充电的方形进度条和一个弧形进度条。

用自定义View展示一个方形进度条

首先创建MyProgress继承View

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;

/**
 * Created by Administrator on 2015/9/16.
 */
public class MyProgress extends View {
    private int width;
    private int height;
    private Paint paint_background;
    private Paint paint_foreground;
    private Paint paint_text;
    private int currentHeight;

    public MyProgress(Context context) {
        super(context);
    }

    public MyProgress(Context context, AttributeSet attrs) {
        super(context, attrs);
        paint_background=new Paint();
        paint_background.setColor(Color.BLUE);//画笔颜色
        paint_background.setStyle(Paint.Style.STROKE);//空心
        paint_background.setAntiAlias(true);//设置抗锯齿

        paint_foreground=new Paint();
        paint_foreground.setColor(Color.GREEN);
        paint_foreground.setAntiAlias(true);

        paint_text=new Paint();
        paint_text.setColor(Color.BLACK);
        paint_text.setTextSize(100);
        paint_text.setAntiAlias(true);
        paint_text.setTextAlign(Paint.Align.CENTER);
    }

    public int getCurrentHeight() {
        return currentHeight;
    }

    public void setCurrentHeight(int currentHeight) {
        this.currentHeight = currentHeight;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        width=getDefaultSize(getSuggestedMinimumWidth(),widthMeasureSpec);
        height=getDefaultSize(getSuggestedMinimumHeight(),heightMeasureSpec);
        setMeasuredDimension(width,height);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        invalidate();
        canvas.drawRect(width/2-100,height/2-200,width/2+100,height/2+200,paint_background);
        canvas.drawRect(width/2+100,height/2+200,width/2-100,height/2+200-currentHeight,paint_foreground);//从下面往上面画
        canvas.drawText(currentHeight*100/400+"%",width/2,height/2,paint_text);
    }
}

然后加入到我们的布局中,这里也要引入全包名

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">
    <Button
        android:id="@+id/button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="开始下载"/>
    <com.example.administrator.mydiywidget.MyProgress
        android:id="@+id/progress"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

最后,修改主活动中的代码

import android.app.Activity;
import android.os.Handler;
import android.os.Message;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

/**
 * Created by Administrator on 2015/9/16.
 */
public class MainActivity extends Activity{
    private int currentHeight;
    private MyProgress myProgress;
    private static final int DOWNLOAD=0x23;
    private Handler handler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case DOWNLOAD:
                    currentHeight++;
                    if (currentHeight<=400){
                        myProgress.setCurrentHeight(currentHeight);
                        handler.sendEmptyMessageDelayed(DOWNLOAD,50);
                    }
                    break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        myProgress= (MyProgress) findViewById(R.id.progress);
        Button button= (Button) findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                handler.sendEmptyMessage(DOWNLOAD);
            }
        });

    }
}

运行结果:点击开始下载按钮
这里写图片描述

用自定义View展示一个弧形进度条

首先创建MyProgress继承View

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;

/**
 * Created by Administrator on 2015/9/16.
 */
public class MyProgress extends View {
    private int width;
    private int height;
    private Paint paint_background;
    private Paint paint_foreground;
    private Paint paint_text;
    private int currentDegree;
    public MyProgress(Context context) {
        super(context);
    }

    public MyProgress(Context context, AttributeSet attrs) {
        super(context, attrs);
        paint_background=new Paint();
        paint_background.setColor(Color.BLUE);//画笔颜色
        paint_background.setStyle(Paint.Style.STROKE);//空心
        paint_background.setAntiAlias(true);//设置抗锯齿
        paint_background.setStrokeWidth(100);

        paint_foreground=new Paint();
        paint_foreground.setColor(Color.GREEN);
        paint_foreground.setStyle(Paint.Style.STROKE);
        paint_foreground.setStrokeWidth(100);
        paint_foreground.setAntiAlias(true);

        paint_text=new Paint();
        paint_text.setColor(Color.BLACK);
        paint_text.setTextSize(100);
        paint_text.setAntiAlias(true);
        paint_text.setTextAlign(Paint.Align.CENTER);
    }

    public int getCurrentDegree() {
        return currentDegree;
    }

    public void setCurrentDegree(int currentDegree) {
        this.currentDegree = currentDegree;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        width=getDefaultSize(getSuggestedMinimumWidth(),widthMeasureSpec);
        height=getDefaultSize(getSuggestedMinimumHeight(),heightMeasureSpec);
        setMeasuredDimension(width,height);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        invalidate();
        RectF rectF=new RectF(width/2-200,height/2-200,width/2+200,height/2+200);
        canvas.drawArc(rectF,0,360,false,paint_background);
        canvas.drawArc(rectF,-90,currentDegree,false,paint_foreground);
        canvas.drawText((int)(currentDegree*100f/360)+"%",width/2,height/2,paint_text);
    }
}

然后将它加入布局,要引入完整的包名

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="开始下载"/>
    <com.example.administrator.mydiywidget.MyProgress
        android:id="@+id/progress"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

最后,修改主活动中的代码

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;

/**
 * Created by Administrator on 2015/9/16.
 */
public class MyainActivity extends Activity{
    private MyProgress myProgress;
    private int currentDegree;
    private static final int DOWNLOAD=0x23;
    private Handler handler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case DOWNLOAD:
                    currentDegree++;
                    if (currentDegree<=360){
                        myProgress.setCurrentDegree(currentDegree);
                        handler.sendEmptyMessageDelayed(DOWNLOAD,50);
                    }
                    break;
            }
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        myProgress= (MyProgress) findViewById(R.id.progress);
        Button button= (Button) findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                handler.sendEmptyMessage(DOWNLOAD);
            }
        });
    }
}

运行结果:点击按钮,进度条开始走动!
这里写图片描述

Canvas和Paint

Canvas类就表示一块画布,Paint类就是一支画笔,我们可以用画笔在画布上面画一些我们想要内容。上边所展示的自定义View内容都是用Canvas和Paint来完成的。下面具体介绍一下它们的用法!

Canvas类常用的方法

Canvas():创建一个空的画布,可以使用setBitmap()方法来设置绘制的具体画布;
Canvas(Bitmap bitmap):以bitmap对象创建一个画布,则将内容都绘制在bitmap上,bitmap不得为null;
drawColor():设置画布的背景色;
setBitmap():设置具体的画布;
clipRect():设置显示区域,即设置裁剪区;
isOpaque():检测是否支持透明;
rotate():旋转画布;
canvas.drawRect(RectF,Paint)方法用于画矩形,第一个参数为图形显示区域,第二个参数为画笔,设置好图形显示区域Rect和画笔Paint后,即可画图;
注意:这里的显示区域Rect(float left,float top,float right,float bottom),参数分别表示左上角的X坐标,Y坐标和右下角的X坐标,Y坐标。
canvas.drawRoundRect(RectF, float, float, Paint)方法用于画圆角矩形,第一个参数为图形显示区域,第二个参数和第三个参数分别是水平圆角半径和垂直圆角半径。
canvas.drawLine(startX, startY, stopX, stopY,paint):前四个参数的类型均为float,最后一个参数类型为Paint。表示用画笔paint从点(startX,startY)到点(stopX,stopY)画一条直线;
canvas.drawArc(oval,startAngle, sweepAngle, useCenter,paint):第一个参数oval为RectF类型,即圆弧显示区域,startAngle和sweepAngle均为float类型,分别表示圆弧起始角度和圆弧度数,3点钟方向为0度,useCenter设置是否显示圆心,boolean类型,paint为画笔;
canvas.drawCircle(float,float, float,Paint)方法用于画圆,前两个参数代表圆心坐标,第三个参数为圆半径,第四个参数是画笔;

Paint类常用的方法

setColor(Color.GRAY)设置画笔颜色
setStrokeWidth(10)设置画笔粗细
setStyle(Paint.Style.STROKE)设置画笔为空心
setAntiAlias(true)设置抗锯齿
setTextSize(30);设置文文文字大小
setTextAlign(Paint.Align.CENTER);设置文字显示位置
另外,画笔还有一个重要的方法setXfermode,它可以实现两个内容叠加,设置不同参数,可以显示不同的效果。用法如下:

//设置效果参数,得到PorterDuffXfermode对象
PorterDuffXfermode mode=new PorterDuffXfermode(PorterDuff.Mode.XOR);
//设置画笔效果,传入PorterDuffXfermode对象
paint.setXfermode(mode);

画一些常用的图形

我们可以绘制圆形,正方形,长方形,圆角矩形,弧形,弧线,三角形,贝塞尔曲线等。
下面给一个例子:

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.View;

/**
 * Created by Administrator on 2015/9/17.
 */
public class MyPathView extends View {
    private int width;
    private int height;
    private Paint paint;
    private Paint paint_point;
    private int index=-40;
    private static final int NEED_INVALIDATE=0x23;
    private Handler handler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case NEED_INVALIDATE:
                    index++;
                    if (index>0){
                        index=-40;//将值设为曲线周期的整数倍,可实现波动效果
                    }
                    invalidate();
                    handler.sendEmptyMessage(NEED_INVALIDATE);
                    break;
            }
        }
    };

    public MyPathView(Context context) {
        super(context);
    }

    public MyPathView(Context context, AttributeSet attrs) {
        super(context, attrs);
        paint = new Paint();
        paint.setTextSize(40);
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.STROKE);

        paint_point = new Paint();
        paint_point.setAntiAlias(true);
        paint_point.setColor(Color.BLUE);
        paint_point.setStrokeWidth(10);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        width = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
        height = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);
        setMeasuredDimension(width, height);
        handler.sendEmptyMessage(NEED_INVALIDATE);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Path path = new Path();
        path.moveTo(100, 100);
        path.lineTo(0, 200);
        path.lineTo(200, 200);
        path.close();//画了一个封闭的三角形
        canvas.drawPath(path, paint);
        canvas.drawTextOnPath("abcdefg", path, 0, 0, paint);//在此路径上面写文本

        Path path1 = new Path();
        path1.addCircle(width / 2, height / 2, 100, Path.Direction.CW);//CW表示顺时针,CCW表示逆时针
        canvas.drawPath(path1, paint);
        canvas.drawTextOnPath("abcdefg", path1, 0, 0, paint);

        Path path2 = new Path();
        path2.moveTo(300, 100);
        path2.quadTo(350, 300, 600, 200);//绘制贝塞尔曲线
        canvas.drawPath(path2, paint);
        canvas.drawPoint(300, 100, paint_point);//起始点
        canvas.drawPoint(350, 300, paint_point);//控制点
        canvas.drawPoint(600, 200, paint_point);//结束点

        Path path3 = new Path();
        path3.moveTo(index, 900);//实现波动效果
        for (int i = 0; i < 50; i++) {
            path3.rQuadTo(10, 5, 20, 0);//绘制贝塞尔曲线,只是此时值从0开始
            path3.rQuadTo(10, -5, 20, 0);
        }
        canvas.drawPath(path3, paint);
    }
}

然后将此View加入布局即可。
运行结果:
这里写图片描述

Bitmap

Bitmap(位图),是Android系统中的图像处理的最重要类之一。用它可以获取图像文件信息,进行图像剪切、旋转、缩放等操作,并可以指定格式保存图像文件。
首先我们在得到Bitmap对象的时候,要借助BitmapFactory来实现。一般有三种不同的方式来得到:

  1. 从资源文件中获取图片
    BitmapFactory.decodeResource(getResources(),R.mipmap.meinv);
  2. 根据文件路径获取图片
    BitmapFactory.decodeFile(filePath, null);
  3. 根据输入流获取图片
    BitmapFactory.decodeStream(inputStream);
    下面自定义一个View,来绘制一个bitmap,来具体介绍一下对图片的平移,放大或缩小,旋转,对称等操作!

首先创建MyBitmapView继承View

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;

/**
 * Created by Administrator on 2015/9/17.
 */
public class MyBitmapView extends View{
    private int width;
    private int height;
    private Paint paint;
    private Bitmap bitmap;
    private int bitmapWidth;
    private int bitmapHeight;
    private Matrix matrix;
    public MyBitmapView(Context context) {
        super(context);
    }

    public MyBitmapView(Context context, AttributeSet attrs) {
        super(context, attrs);
        paint=new Paint();
        bitmap= BitmapFactory.decodeResource(getResources(),R.mipmap.meinv);//创建Bitmap对象
        bitmapWidth=bitmap.getWidth();//得到图片的宽
        bitmapHeight=bitmap.getHeight();//得到图片的高
        matrix=new Matrix();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        width=getDefaultSize(getSuggestedMinimumWidth(),widthMeasureSpec);
        height=getDefaultSize(getSuggestedMinimumHeight(),heightMeasureSpec);
        setMeasuredDimension(width,height);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawBitmap(bitmap,0,0,paint);

        matrix.reset();
        matrix.postScale(2,2);//放大或缩小的倍数
        matrix.postTranslate(0,bitmapHeight);//平移的距离
        canvas.drawBitmap(bitmap,matrix,paint);

        matrix.reset();
        matrix.postRotate(150,bitmapWidth,bitmapHeight);//旋转的角度
        canvas.drawBitmap(bitmap,matrix,paint);

        matrix.reset();
        float matrix_values_x[]={1f,0f,0f,0f,-1f,0f,0f,0f,1f};//关于X轴对称  倒影效果
        matrix.setValues(matrix_values_x);
        matrix.postTranslate(0,bitmapHeight*2);
        canvas.drawBitmap(bitmap,matrix,paint);

        matrix.reset();
        float matrix_values_y[]={-1f,0f,0f,0f,1f,0f,0f,0f,1f};//关于Y轴对称  镜面效果
        matrix.setValues(matrix_values_y);
        matrix.postTranslate(bitmapWidth*2,0);
        canvas.drawBitmap(bitmap,matrix,paint);
    }
}

我们可以把此View放在我们的布局中可以看一下效果!需要说明的是上边写的关于X,Y轴对称,图片翻转的对称轴为图片上面的边和图片左边的边。

仿手机联系人字母索引

首先MySlider继承View

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

/**
 * Created by Administrator on 2015/9/17.
 */
public class MySlider extends View {
    private int width;
    private int height;
    private Paint paint;
    private Paint paint_choose;
    private float x;
    private float y;
    private int wordHeight = -1;
    private char[] array = new char[26];

    public MySlider(Context context) {
        super(context);
    }

    public MySlider(Context context, AttributeSet attrs) {
        super(context, attrs);
        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setTextAlign(Paint.Align.CENTER);
        paint_choose = new Paint();
        paint_choose.setAntiAlias(true);
        paint_choose.setTextAlign(Paint.Align.CENTER);
        paint_choose.setColor(Color.GREEN);
    }

    //观察者模式
    public interface OnItemSelect{
        public void OnItemSelected(String selectText);//参数为当前选中的字母
    }
    private OnItemSelect onItemSelectListener;

    public void setOnItemSelectListener(OnItemSelect onItemSelectListener) {
        this.onItemSelectListener = onItemSelectListener;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_MOVE:
                break;
            case MotionEvent.ACTION_DOWN:
                x = event.getX();
                y = event.getY()-height/26/2;//将点击位置的Y坐标稍微下移
                if (x > width - paint.measureText("m") * 2) {
                    wordHeight = (int)y/(height / 26);//得到当前选中的索引
                    onItemSelectListener.OnItemSelected(array[wordHeight]+"");//调用此方法,传入当前选中的字母
                    invalidate();
                    return true;
                }
                break;
            case MotionEvent.ACTION_UP:
                wordHeight = -1;
                invalidate();
                break;
        }
        return super.onTouchEvent(event);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        width = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
        height = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);
        setMeasuredDimension(width, height);
        paint.setTextSize(height / 26f);//height在onMeasure方法中才有值
        paint_choose.setTextSize(height / 26f);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        for (int i = 97; i <= 122; i++) {
            array[i - 97] = (char) i;
        }
        for (int i = 0; i < 26; i++) {
            if (wordHeight == i) {//如果当前被选中,设置为蓝色画笔绘制
                canvas.drawText("" + array[i], width - paint.measureText("m"), height / 26f * (i + 1), paint_choose);
                invalidate();
            } else {
                canvas.drawText("" + array[i], width - paint.measureText("m"), height / 26f * (i + 1), paint);
            }
        }
    }
}

然后在布局中加入一个TextView来展示当前选中的字母

<RelativeLayout  xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
               <TextView
                   android:id="@+id/textview"
                   android:layout_width="match_parent"
                   android:layout_height="match_parent"
                   android:gravity="center"
                   android:textSize="300dp"
                   android:text="a"/>
               <com.example.administrator.mydiywidget.MySlider
                   android:id="@+id/myslider"
                   android:layout_width="match_parent"
                   android:layout_height="match_parent" />

</RelativeLayout>

最后修改主活动中的代码:

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

/**
 * Created by Administrator on 2015/9/17.
 */
public class MainActivity extends Activity{
    private TextView textView;
    private MySlider mySlider;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView= (TextView) findViewById(R.id.textview);
        mySlider= (MySlider) findViewById(R.id.myslider);
        mySlider.setOnItemSelectListener(new MySlider.OnItemSelect() {
            @Override
            public void OnItemSelected(String selectText) {
                textView.setText(selectText);//得到选中的字母,设置到TextView上面显示
            }
        });
    }
}

运行效果:
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值