自定义控件玩套路以及canvas StaticLayout的使用
Posted 灬弘法大师丶
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自定义控件玩套路以及canvas StaticLayout的使用相关的知识,希望对你有一定的参考价值。
遇到自定义控件的时候很苦恼、不知道从哪里下手、鄙人也是新手、记录下开发的思路
我们有时候需要把某一个功能封装成控件、很简单、写好布局、将其inflate出来
必要的属性、就跟普通的activity一样定义即可、context直接调用getContext();
public class DanmakuChannel extends RelativeLayout {
public boolean isRunning = false;
public DanmakuEntity mEntity;
private DanmakuActionInter danAction;
public DanmakuActionInter getDanAction() {
return danAction;
}
public void setDanAction(DanmakuActionInter danAction) {
this.danAction = danAction;
}
public DanmakuChannel(Context context) {
super(context);
init();
}
public DanmakuChannel(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public DanmakuChannel(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public DanmakuChannel(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
private void init() {
LayoutInflater inflater = LayoutInflater.from(getContext());
inflater.inflate(R.layout.danmaku_channel_layout, null);
}
}
定义完了、在布局中调用即可
<com.just.sun.widget.danmu.DanmuBase.DanmakuChannel
android:id="@+id/danA"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:layout_marginTop="10dp" />
要处理点击事件、很简单、可以findViewById setOnclickListener、跟Activity是一样的、
如果更麻烦一点要捕获触摸事件、需要重写方法
下面我通过点击做了个小动画、点击屏幕会出现晃动的小手、然后消失
public View.OnTouchListener onLightTouchListener = new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (model.liveEntity != null) {
float x = event.getX();
float y = event.getY();
final int action = MotionEventCompat.getActionMasked(event);
switch (action) {
case MotionEvent.ACTION_DOWN://单点触摸动作
break;
case MotionEvent.ACTION_UP://单点触摸离开动作
ImageView imageView = new ImageView(binding.getRoot().getContext());
imageView.setImageResource(R.drawable.light_touch);
final int width = imageView.getWidth();
final int height = imageView.getHeight();
//如果在边缘就不绘制
if (binding.getRoot().getWidth() - x > 130
&& binding.getRoot().getHeight() - y > 130) {
sengLight();
RelativeLayout.LayoutParams param = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
imageView.setLayoutParams(param);
param.setMargins((int) x - 130 / 2, (int) y - 130 / 2, 0, 0);
binding.zanContainer.addView(imageView);
startAnim(x, y, imageView);
}
break;
case MotionEvent.ACTION_MOVE://触摸点移动动作
break;
case MotionEvent.ACTION_CANCEL://触摸动作取消
break;
case MotionEvent.ACTION_POINTER_DOWN://多点触摸动作
break;
case MotionEvent.ACTION_POINTER_UP://多点离开动作
break;
}
return true;
}
return true;
}
};
通过上面的case大家看到了、我获取控件的高宽是通过 getWidth()
但是有时候、获取到是0、是因为控件还没有绘制完成、那么我们可以
view.measure(-1, -1);
int measuredWidth = view.getMeasuredWidth();
如果我们自定义控件、需要canvas画的时候、获取到文字或者是bitmap的高宽很重要、
因为通过自定义坐标来定义画的起始位置、默认位置是 0,0 也就是左上角
画文字 我么常用到StaticLayout、下面讲一下StaticLayout
TextView就是用StaticLayout画的
他有三个构造函数
public StaticLayout(CharSequence source,
TextPaint paint,
int width,
Layout.Alignment align,
float spacingmult,
float spacingadd,
boolean includepad)
public StaticLayout(CharSequence source,
int bufstart,
int bufend,
TextPaint paint,
int outerwidth,
Layout.Alignment align,
float spacingmult,
float spacingadd,
boolean includepad)
public StaticLayout(CharSequence source,
int bufstart,
int bufend,
TextPaint paint,
int outerwidth,
Layout.Alignment align,
float spacingmult,
float spacingadd,
boolean includepad,
TextUtils.TruncateAt ellipsize,
int ellipsizedWidth)
最后一个构造参数讲解一下
1.需要分行的字符串
2.需要分行的字符串从第几的位置开始
3.需要分行的字符串到哪里结束
4.画笔对象
5.layout的宽度,字符串超出宽度时自动换行。
6.layout的对其方式,有ALIGN_CENTER, ALIGN_NORMAL, ALIGN_OPPOSITE 三种。
7.相对行间距,相对字体大小,1.5f表示行间距为1.5倍的字体高度。
8.在基础行距上添加多少
实际行间距等于这两者的和。
9.参数未知
10.从什么位置开始省略
11.超过多少开始省略
通常是这么用的
StaticLayout staticLayout = new StaticLayout(text, paint, (int) Math.ceil(StaticLayout.getDesiredWidth(text, paint)), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, true);
width = staticLayout.getWidth();
height = staticLayout.getHeight();
getDesiredWidth(text, paint) 是Layout的方法、取得字符串显示的宽度。
也可以这样获取宽度
TextPaint paint = new TextPaint();
paint.setTextSize(textSize);
float contentLength = paint.measureText(content, 0, content.length());
return contentLength;
//这个方法 如果使用了ImageSpan会失效、建议用上一种方法
下面搞一下 canvas、站在巨人的肩膀上
一个画图需要四大基本要素:
1、一个用来保存像素的Bitmap;
2、一个Canvas画布,绘制Bitmap操作;
3、绘制的东西
4、绘制的画笔Paint(颜色和样式)
1、如何获得一个Canvas对象。
Canvas对象的获取方式有三种:
第一种我们通过重写View.onDraw方法,View中的Canvas对象会被当做参数传递过来,我们操作这个Canvas,效果会直接反应在View中。
第二种就是当你想自己创建一个Canvas对象。从上面的基本要素可以明白,一个Canvas对象一定是结合了一个Bitmap对象的。所以一定要为一个Canvas对象设置一个Bitmap对象。
//得到一个Bitmap对象,当然也可以使用别的方式得到。但是要注意,改bitmap一定要是mutable(异变的)
Bitmap b = Bitmap.createBitmap(100,100, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
/**先new一个Canvas对象,在调用setBitmap方法,一样的效果
* Canvas c = new Canvas();
* c.setBitmap(b);
*/
第三种方式,是调用SurfaceHolder.lockCanvas(),返回一个Canvas对象。
2、canvas可以绘制的内容
1)填充
drawARGB(int a, int r, int g, int b)
drawColor(int color)
drawRGB(int r, int g, int b)
drawColor(int color, PorterDuff.Mode mode)
2)几何图形
canvas.drawArc (扇形)
canvas.drawCircle(圆)
canvas.drawOval(椭圆)
canvas.drawLine(线)
canvas.drawPoint(点)
canvas.drawRect(矩形)
canvas.drawRoundRect(圆角矩形)
canvas.drawVertices(顶点)
cnavas.drawPath(路径)
3)图片
canvas.drawBitmap (位图)
canvas.drawPicture (图片)
4)文本
canvas.drawText
3. Canvas的保存和回滚
为了方便一些转换操作,Canvas还提供了保存和回滚属性的方法(save和restore),比如你可以先保存目前画纸的位置(save),然后旋转90度,向下移动100像素后画一些图形,画完后调用restore方法返回到刚才保存的位置。
Canvas提供的该功能的API如下:
/**
* 保存当前的matrix和clip到私有的栈中(Skia内部实现)。任何matrix变换和clip操作都会在调用restore的时候还原。
* @return 返回值可以传入到restoreToCount()方法,以返回到某个save状态之前。
*/
public native int save();
/**
* 传入一个标志,来表示当restore 的时候,哪些参数需要还原。该参数定义在Canvas中,参照下面。
* save()方法默认的是还原matrix和clip,但是可以使用这个方法指定哪些需要还原。并且只有指定matrix和clip才有效,其余的几个参数是
* 用于saveLayer()和saveLayerAlpha()方法 的。
*/
public native int save(int saveFlags);
/**
* 回到上一个save调用之前的状态,如果restore调用的次数大于save方法,会出错。
*/
public native void restore();
/**
* 返回栈中保存的状态,值等译 save()调用次数-restore()调用次数
*/
public native int getSaveCount();
/**
* 回到任何一个save()方法调用之前的状态
*/
public native void restoreToCount(int saveCount);
/**saveFlags的参数*/
public static final int MATRIX_SAVE_FLAG = 0x01;//需要还原Matrix
public static final int CLIP_SAVE_FLAG = 0x02;//需要还原Clip
/**下面三个参数在saveLayer的时候使用,具体作用,没有搞明白*/
public static final int HAS_ALPHA_LAYER_SAVE_FLAG = 0x04;
public static final int FULL_COLOR_LAYER_SAVE_FLAG = 0x08;
public static final int CLIP_TO_LAYER_SAVE_FLAG = 0x10;
public static final int ALL_SAVE_FLAG = 0x1F; //还原所有
/*关于saveLayer的具体flags还不大明白它的含义,具体怎么使用在下面例子中*/
public int saveLayer(RectF bounds, Paint paint, int saveFlags)
public int saveLayer(float left, float top, float right, float bottom,
Paint paint, int saveFlags)
public int saveLayerAlpha(RectF bounds, int alpha, int saveFlags)
public int saveLayerAlpha(float left, float top, float right, float bottom,
int alpha, int saveFlags)
saveLayer
Canvas 在一般的情况下可以看作是一张画布,所有的绘图操作如drawBitmap, drawCircle都发生在这张画布上,这张画板还定义了一些属性比如Matrix,颜色等等。但是如果需要实现一些相对复杂的绘图操作,比如多层动画,地图(地图可以有多个地图层叠加而成,比如:政区层,道路层,兴趣点层)。Canvas提供了图层(Layer)支持,缺省情况可以看作是只有一个图层Layer。如果需要按层次来绘图,Android的Canvas可以使用SaveLayerXXX, Restore 来创建一些中间层,对于这些Layer是按照“栈结构“来管理的:
创建一个新的Layer到“栈”中,可以使用saveLayer, savaLayerAlpha, 从“栈”中推出一个Layer,可以使用restore,restoreToCount。但Layer入栈时,后续的DrawXXX操作都发生在这个Layer上,而Layer退栈时,就会把本层绘制的图像“绘制”到上层或是Canvas上,在复制Layer到Canvas上时,可以指定Layer的透明度(Layer),这是在创建Layer时指定的:public int saveLayerAlpha(RectF bounds, int alpha, int saveFlags)本例Layers 介绍了图层的基本用法:Canvas可以看做是由两个图层(Layer)构成的,为了更好的说明问题,我们将代码稍微修改一下,缺省图层绘制一个红色的圆,在新的图层画一个蓝色的圆,新图层的透明度为0×88。
public class Layers extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new SampleView(this));
}
private static class SampleView extends View {
private static final int LAYER_FLAGS = Canvas.MATRIX_SAVE_FLAG | Canvas.CLIP_SAVE_FLAG
| Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.FULL_COLOR_LAYER_SAVE_FLAG
| Canvas.CLIP_TO_LAYER_SAVE_FLAG;
private Paint mPaint;
public SampleView(Context context) {
super(context);
setFocusable(true);
mPaint = new Paint();
mPaint.setAntiAlias(true);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.WHITE);
canvas.translate(10, 10);
mPaint.setColor(Color.RED);
canvas.drawCircle(75, 75, 75, mPaint);
canvas.saveLayerAlpha(0, 0, 200, 200, 0x88, LAYER_FLAGS);
mPaint.setColor(Color.BLUE);
canvas.drawCircle(125, 125, 75, mPaint);
canvas.restore();
}
}
}
以上是关于自定义控件玩套路以及canvas StaticLayout的使用的主要内容,如果未能解决你的问题,请参考以下文章