自定义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来实现。一般有三种不同的方式来得到:
- 从资源文件中获取图片
BitmapFactory.decodeResource(getResources(),R.mipmap.meinv); - 根据文件路径获取图片
BitmapFactory.decodeFile(filePath, null); - 根据输入流获取图片
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上面显示
}
});
}
}
运行效果: