自定义布局简单分类:
一、自绘控件
这里我们实现一个可以沿着圆周来旋转的飞机图标
首先了解一下PathMeasuer这个类:
定义:用来测量Path的类,可以理解为专门对Path每一个点获取信息的类
优势:可计算每一点正切、余切值,也可截取某一段Path单独对它绘制;
常用方法:
PathMeasure()初始化只需要new一个PathMeasure对象即可,初始化PathMeasure后,可以通过PathMeasure.setPath()的方式来将Path和PathMeasure进行绑定
PathMeasure (Path path, boolean forceClosed)path,和setPath()同样的作用,forceClosed就是Path最终是否需要闭合,如果为True的话,则不管关联的Path是否是闭合的,都会被闭合
getLength获取计算的路径长度
boolean getSegment (float startD, float stopD, Path dst, boolean startWithMoveTo)截取整个Path的片段,通过参数startD和stopD来控制截取的长度,并将截取的Path保存到dst中,最后一个参数startWithMoveTo表示起始点是否使用moveTo方法,通常为True,保证每次截取的Path片段都是正常的、完整的。
boolean getPosTan (float distance, float[] pos, float[] tan)通过指定distance(0<distance<getLength),来获取坐标点和切线的坐标,并保存到pos[]和tan[]数组中。
这里对我们将用到的方法getPosTan()详细图解一下:
代码实现:
package com.xxx.uidemo;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.util.AttributeSet;
import android.view.View;
public class RotateView extends View {
private Bitmap bitmap;// 旋转图标
private Path path;//旋转路径
private PathMeasure pathMeasure;//路径测量
private float distanceRatio;// 旋转进度
private Paint circlePaint;// 画圆圈的画笔
private Paint iconPaint;// 画图标的画笔
private Matrix matrix = new Matrix();// 图标bitmap图片操作矩阵
public RotateView(Context context) {
this(context, null);
}
public RotateView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public RotateView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.icon_airplane);// 初始化图标
path = new Path();//初始化path
path.addCircle(0, 0, 300, Path.Direction.CW);
pathMeasure = new PathMeasure(path, false);//将圆的path和pathMeasure绑定起来
circlePaint = new Paint();
circlePaint.setColor(Color.BLACK);
circlePaint.setStyle(Paint.Style.STROKE);
circlePaint.setStrokeWidth(5);
circlePaint.setAntiAlias(true);
iconPaint = new Paint();
iconPaint.setColor(Color.DKGRAY);
iconPaint.setStyle(Paint.Style.STROKE);
iconPaint.setStrokeWidth(2);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.WHITE);
int width = canvas.getWidth();
int height = canvas.getHeight();
canvas.translate(width / 2, height / 2);
matrix.reset();
distanceRatio += 0.006f;
if (distanceRatio >= 1) {
distanceRatio = 0;
}
float pos[] = new float[2];// 记录P点坐标
float tan[] = new float[2];// 记录P点正切值,tan[0] = cosA ,tan[1] = sinA;
float distance = pathMeasure.getLength() * distanceRatio;
//获取pos和tan值
pathMeasure.getPosTan(distance, pos, tan);
//计算P点要旋转的角度
float degree = (float) (Math.atan2(tan[1], tan[0]) * 180 / Math.PI);//计算角度使用Math.atan2(tan[1], tan[0]),然后再转化为真正意义上的角度
matrix.postRotate(degree, bitmap.getWidth() / 2, bitmap.getHeight() / 2);// 设置旋转角度
matrix.postTranslate(pos[0] - bitmap.getWidth() / 2, pos[1] - bitmap.getHeight() / 2);// 设置图标中心点,确保在圆周轨迹上
canvas.drawPath(path, circlePaint);//画圆形
canvas.drawBitmap(bitmap, matrix, iconPaint);//画图标
invalidate();
}
}
展示效果:
二、组合控件
这里我们实现一个应用菜单栏,相当于ToolBar的作用
由于比较简单,这里我们直接贴代码:
首先自定义实现ToolBar
package com.xxx.uidemo;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
public class ToolBar extends RelativeLayout {
private ImageView ivBack;
private ImageView ivMenu;
private ImageView ivMore;
private TextView tvTitle;
private int titleColor = Color.BLACK;
private String title;
public ToolBar(Context context) {
this(context, null);
}
public ToolBar(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ToolBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ToolBar);
titleColor = typedArray.getColor(R.styleable.ToolBar_titleColor, Color.BLACK);
title = typedArray.getString(R.styleable.ToolBar_title);
typedArray.recycle();
LayoutInflater.from(context).inflate(R.layout.tool_bar, this, true);
ivBack = findViewById(R.id.iv_back);
ivMenu = findViewById(R.id.iv_menu);
ivMore = findViewById(R.id.iv_more);
tvTitle = findViewById(R.id.tv_title);
setTitle(titleColor, title);
}
public void setBackClickListener(OnClickListener onClickListener) {
ivBack.setOnClickListener(onClickListener);
}
public void setMenuClickListener(OnClickListener onClickListener) {
ivMenu.setOnClickListener(onClickListener);
}
public void setMoreClickListener(OnClickListener onClickListener) {
ivMore.setOnClickListener(onClickListener);
}
private void setTitle(int color, String title) {
tvTitle.setTextColor(color);
if (!TextUtils.isEmpty(title)) {
tvTitle.setText(title);
}
}
}
自定义属性文件attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ToolBar">
<attr name="titleColor" format="color"></attr>
<attr name="title" format="string"></attr>
</declare-styleable>
</resources>
ToolBar的布局文件tool_bar.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="50dp">
<ImageView
android:id="@+id/iv_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_marginLeft="2dp"
android:padding="5dp"
android:src="@mipmap/back_icon" />
<ImageView
android:id="@+id/iv_menu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toRightOf="@id/iv_back"
android:padding="5dp"
android:src="@mipmap/menu_icon" />
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="标题"
android:textSize="22sp" />
<ImageView
android:id="@+id/iv_more"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="2dp"
android:padding="5dp"
android:src="@mipmap/more_icon" />
</RelativeLayout>
接下来就是使用啦
activity_test.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.xxx.uidemo.ToolBar
android:id="@+id/tb_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:title="首页展示"
app:titleColor="@color/colorPrimaryDark"></com.yan.uidemo.ToolBar>
</RelativeLayout>
在onCreate生命周期中使用
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
ToolBar toolBar = findViewById(R.id.tb_bar);
toolBar.setBackClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(MainActivity.this, "点击返回", Toast.LENGTH_SHORT).show();
}
});
toolBar.setMenuClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(MainActivity.this, "点击菜单", Toast.LENGTH_SHORT).show();
}
});
toolBar.setMoreClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(MainActivity.this, "点击更多", Toast.LENGTH_SHORT).show();
}
});
}