自定义布局简单分类:
一、自绘控件
这里我们实现一个可以沿着圆周来旋转的飞机图标
首先了解一下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 这里对我们将用到的方法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 ToolBar的布局文件tool_bar.xml android:layout_width="match_parent" android:layout_height="50dp"> 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" /> 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" /> 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" /> 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" /> 接下来就是使用啦 activity_test.xml xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> android:id="@+id/tb_bar" android:layout_width="match_parent" android:layout_height="wrap_content" app:title="首页展示" app:titleColor="@color/colorPrimaryDark"> 在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(); } }); }