为什么需要自定义控件?
由于安卓系统控件提供的属性和api不足以满足项目需求中要求的效果时,通常需要采用自定义控件的方式来完成需求(貌似有点简洁,不过应该是说道了重点吧)
自定义控件分为哪几种?
1.组合控件
采用Linearlayout,RelativeLayout,FrameLayout加上自定义xml所形成的view,下面是一段举例代码
package tv.douyu.newpk;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.LinearInterpolator;
import android.view.animation.TranslateAnimation;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.douyu.lib.utils.DYDensityUtils;
import com.douyu.lib.utils.DYNumberUtils;
import com.douyu.module.link.R;
import com.orhanobut.logger.MasterLog;
import tv.douyu.utils.LinkPkHelper;
public class UnPkProgress extends FrameLayout {
public UnPkProgress(@NonNull Context context) {
this(context, null);
}
public UnPkProgress(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public UnPkProgress(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
private LinearLayout li;
private TextView tvLeft;
private TextView tvRight;
private TextView homeProgressValue;
private TextView guestProgressValue;
private ImageView ivFlashLeft;
private ImageView ivFlashRight;
private boolean hasLeft;
private boolean hasRight;
private void initView() {
View.inflate(getContext(), R.layout.view_un_pk_progress, this);
li = findViewById(R.id.un_pk_pr_li);
tvLeft = findViewById(R.id.left_progress);
tvRight = findViewById(R.id.right_progress);
homeProgressValue = findViewById(R.id.home_progress_value);
guestProgressValue = findViewById(R.id.guest_progress_value);
ivFlashLeft = findViewById(R.id.iv_flash_left);
ivFlashRight = findViewById(R.id.iv_flash_right);
}
public void updateProgress(final String sc, final String sc1) {
tvLeft.setBackgroundResource(R.drawable.bg_gradient_up_red);
tvRight.setBackgroundResource(R.drawable.bg_gradient_up_blue);
li.post(new Runnable() {
@Override
public void run() {
long left = DYNumberUtils.parseLongByCeil(sc) / 100;
long right = DYNumberUtils.parseLongByCeil(sc1) / 100;
long total = left + right;
float lw = (float) left / total;
float rw = (float) right / total;
int tw = li.getWidth();
if (tw == 0) {
tw = DYDensityUtils.dip2px(224);//UI给的固定长度
}
if (left == 0 && right == 0) {
tvLeft.setLayoutParams(new LinearLayout.LayoutParams(tw / 2, LayoutParams.MATCH_PARENT));
tvRight.setLayoutParams(new LinearLayout.LayoutParams(tw / 2, LayoutParams.MATCH_PARENT));
} else {
//如果有一方为0,另一方不为0,为0点一行按0.1来处理进度条
if (lw < 0.2 && lw > 0) {
lw = 0.2f;
rw = 0.8f;
}
if (rw < 0.2 && rw > 0) {
rw = 0.2f;
lw = 0.8f;
}
if (lw == 1 && rw == 0) {
lw = 0.93f;
rw = 0.07f;
}
if (rw == 1 && lw == 0) {
rw = 0.93f;
lw = 0.07f;
}
tvLeft.setLayoutParams(new LinearLayout.LayoutParams((int) (tw * lw), LayoutParams.MATCH_PARENT));
tvRight.setLayoutParams(new LinearLayout.LayoutParams((int) (tw * rw), LayoutParams.MATCH_PARENT));
}
}
});
homeProgressValue.setText(LinkPkHelper.getPkSc(sc)); //截断,没有四舍五入
guestProgressValue.setText(LinkPkHelper.getPkSc(sc1));
//重新计算偏移量
ivFlashLeft.clearAnimation();
ivFlashRight.clearAnimation();
showFlash(hasLeft,hasRight);
}
/**
* @param isLeft true:左边胜利
* false:右边胜利
*/
public void showResult(boolean isLeft) {
if (isLeft) {
tvRight.setBackgroundResource(R.drawable.bg_gradient_up_r_gray);
} else {
tvLeft.setBackgroundResource(R.drawable.bg_gradient_up_l_gray);
}
}
/**
* 流光效果展示
*/
public void showFlash(boolean hasLeft, boolean hasRight) {
this.hasLeft=hasLeft;
this.hasRight=hasRight;
if (hasLeft) {
ivFlashLeft.setVisibility(VISIBLE);
li.postDelayed(new Runnable() {
@Override
public void run() {
int mViewWidth = tvLeft.getWidth();
int mFlashWidth = ivFlashLeft.getWidth();
MasterLog.d("mFlashWidth", "mFlashWidth:" + mFlashWidth + "mViewWidth:" + mViewWidth);
//闪光组合动画
AnimationSet set = new AnimationSet(true);
AlphaAnimation alpha = new AlphaAnimation(1, 0.6f);
alpha.setRepeatCount(Animation.INFINITE);
TranslateAnimation translate = new TranslateAnimation(0, mViewWidth - mFlashWidth-10, 0, 0);
translate.setRepeatCount(Animation.INFINITE);
set.addAnimation(alpha);
set.addAnimation(translate);
set.setDuration(3000);
set.setInterpolator(new LinearInterpolator());
ivFlashLeft.startAnimation(set);
}
}, 50);
}
if (hasRight) {
ivFlashRight.setVisibility(VISIBLE);
li.post(new Runnable() {
@Override
public void run() {
int mViewWidth = tvRight.getWidth();
int mFlashWidth = ivFlashRight.getMeasuredWidth();
MasterLog.d("mFlashWidth", mViewWidth + "");
//闪光组合动画
AnimationSet set = new AnimationSet(true);
AlphaAnimation alpha = new AlphaAnimation(1, 0.6f);
alpha.setRepeatCount(Animation.INFINITE);
TranslateAnimation translate = new TranslateAnimation(0, -mViewWidth +mFlashWidth+10, 0, 0);
translate.setRepeatCount(Animation.INFINITE);
set.addAnimation(alpha);
set.addAnimation(translate);
set.setDuration(3000);
set.setInterpolator(new LinearInterpolator());
ivFlashRight.startAnimation(set);
}
});
}
}
/**
* 停止流光效果
*/
public void stopFlash() {
hasRight=false;
hasLeft=false;
ivFlashLeft.setVisibility(GONE);
ivFlashLeft.clearAnimation();
ivFlashRight.setVisibility(GONE);
ivFlashRight.clearAnimation();
}
}
2.单个view所形成的控件
package com.example.myapplication;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.Gravity;
/**
* 文字旋转
*/
public class LeanTextView extends androidx.appcompat.widget.AppCompatTextView {
public int getmDegrees() {
return mDegrees;
}
public void setmDegrees(int mDegrees) {
this.mDegrees = mDegrees;
invalidate();
}
private int mDegrees;//倾斜度
public LeanTextView(Context context) {
super(context, null);
}
public LeanTextView(Context context, AttributeSet attrs) {
super(context, attrs, android.R.attr.textViewStyle);
this.setGravity(Gravity.CENTER);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.LeanTextView);
mDegrees = a.getDimensionPixelSize(R.styleable.LeanTextView_degree, 0);
a.recycle();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(getMeasuredWidth(), getMeasuredWidth());
}
@Override
protected void onDraw(Canvas canvas) {
canvas.save();
canvas.translate(getCompoundPaddingLeft(), getExtendedPaddingTop());
canvas.rotate(mDegrees, this.getWidth() / 2f, this.getHeight() / 2f);
super.onDraw(canvas);
canvas.restore();
}
}
3.集成ViewGroup自定义的组合控件
package com.douyu.module.energy.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.douyu.module.energy.R;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 云标签
*/
public class TagsView extends ViewGroup {
public interface Adapter {
View getView(TagsView tagsView, int pos);
int getCount();
void onShowCross(int pos, View tagChildView, boolean isShow);
}
public TagsView(Context context) {
this(context, null);
}
public TagsView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TagsView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs, defStyleAttr);
}
public void setOnTagListener(OnTagClickListener tagListener) {
this.tagListener = tagListener;
}
private void init(Context context, AttributeSet attrs, int defStyleAttr) {
//初始化...
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//测量...
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
//布局...
}
//其他若干实现方法..
}
4.复杂自定义控件
列表滑动,屏幕拖动,特效炫技,结合到算法的动态view
上面是通过代码层面来定义自定义控件的,通过产品和UI形态来定义的话,简单点说,就是静态与动态
特别炫的动效,不建议个人绘制,直接让UI出个svga,gift性能加载不好,也不建议,当然项目不大的情况下,无关乎性能的话,就随意。
自定义控件基础方法与类
方法 1.构造方法实现 做过自定义的朋友应该都知道的,自定义控件必须实现3个构造方法,那么为什么要实现它们呢? 2.onMeasure
3.onLayout
4.onDraw