最近同事需要实现如下效果的一个功能,我闲来无事就试着实现了一下,以下是具体功能的实现步骤:
功能要求:
- 可以水平滚动
- 动态添加个数不定
- 可点击 点击后变为选中色
功能实现:
- 自定义View
package com.light.mytext.mycustomview;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.light.mytext.R;
import com.light.mytext.utiles.DensityUtil;
import java.util.ArrayList;
/**
* Created by HARRY on 2019/1/18 0018.
*/
public class DiscussionAvatarView extends ViewGroup implements View.OnClickListener {
/**
* 图片的宽高
*/
private int mItemViewWidth;
private int mItemViewHeight;
/**
* 头像间的距离
*/
private float mSpace;
private Context mContext;
private LayoutInflater mInflater;
/**
* 是否最后一个显示完全
*/
private boolean mIsLastComplete;
/**
* 最大头像数目
*/
private int mMaxCount;
/**
* 当前移动的偏移量
*/
private int mCurrentOffset;
/**
* 移动的属性动画
*/
private ValueAnimator animator;
/**
* 是否显示动画效果
*/
private boolean mIsShowAnimation;
/**
* 监听
*/
private boolean mIsShowFrame;
private int mFrameColor;
//重写点击事件
private OnItemViewClickListener mOnItemClickListener = null;//点击事件
//选中图片R.mipmap.anniu115
private Integer[] pic = {
R.mipmap.anniu5, R.mipmap.anniu511};
//选中的index
private int ChooseIndex = 0;
private ArrayList<String> listData = new ArrayList();
public DiscussionAvatarView(Context context) {
this(context, null);
}
public DiscussionAvatarView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public DiscussionAvatarView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context, attrs);
}
private void initView(Context context, AttributeSet attrs) {
this.mContext = context;
this.mInflater = LayoutInflater.from(mContext);
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.DiscussionAvatarView);
if (array != null) {
mSpace = array.getFloat(R.styleable.DiscussionAvatarView_space, (float) 0.8);
mMaxCount = array.getInteger(R.styleable.DiscussionAvatarView_maxCount, 6);
mIsLastComplete = array.getBoolean(R.styleable.DiscussionAvatarView_isLastComplete, true);
mIsShowAnimation = array.getBoolean(R.styleable.DiscussionAvatarView_isShowAnimation, true);
mIsShowFrame = array.getBoolean(R.styleable.DiscussionAvatarView_isShowFrame, true);
mFrameColor = array.getColor(R.styleable.DiscussionAvatarView_frameColor, Color.RED);
mItemViewWidth = array.getInteger(R.styleable.DiscussionAvatarView_iviewWidth, 150);//item的宽
mItemViewHeight = array.getInteger(R.styleable.DiscussionAvatarView_iviewHeight, 60);//item的高
array.recycle();
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int heiMeasure = MeasureSpec.getSize(heightMeasureSpec);
int heiMode = MeasureSpec.getMode(heightMeasureSpec);
int widMode = MeasureSpec.getMode(widthMeasureSpec);
int widMeasure = MeasureSpec.getSize(widthMeasureSpec);
int wid = 0;
int hei = 0;
int count = getChildCount();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
LayoutParams lp = child.getLayoutParams();
lp.width = DensityUtil.dip2px(mContext, mItemViewWidth);
lp.height = DensityUtil.dip2px(mContext, mItemViewHeight);
child.setLayoutParams(lp);
// 测量子View的宽和高,系统提供的measureChild
measureChild(child, widthMeasureSpec, heightMeasureSpec);
// 子View占据的宽度
int childWidth = child.getMeasuredWidth();
// 子View占据的高度
int childHeight = child.getMeasuredHeight();
if (i < mMaxCount) {
if (i == 0) {
wid = wid + childWidth;
} else {
wid = (int) (wid + childWidth * mSpace);
}
}
hei = Math.max(hei, childHeight);
child.setOnClickListener(this);
}
//如果是exactly使用测量宽和高,否则使用自己设置的宽和高
setMeasuredDimension((widMode == MeasureSpec.EXACTLY) ? widMeasure : wid,
(heiMode == MeasureSpec.EXACTLY) ? heiMeasure : hei);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int count = getChildCount();
int left = -mCurrentOffset;
int top = 0;
int right = -mCurrentOffset;
for (int i = count - 1; i > -1; i--) {
//配合第一个元素置顶效果 数据反着放 间距也反着计算 左边叠加右边
View child;
child = getChildAt(i);
int childWidth = child.getMeasuredWidth();
int childHeight = child.getMeasuredHeight();
if (i == count - 1) {
right = right + childWidth;
} else {
right = (int) (right + childWidth * mSpace);
}
child.layout(left, top, right, childHeight);
left = (int) (left + childWidth * mSpace);
}
}
/**
* 初始化数据
*
* @param list
*/
public void initDatasList(ArrayList<String> list) {
if (list == null) {
return;
}
listData = list;
initDatas();
}
/**
* 初始化数据
*/
public void initDatas() {
if (listData == null) {
return;
}
removeAllViews();
int size = listData.size();
mMaxCount = size;
for (int i = size - 1; i > -1; i--) {
//反向添加 设置最左边置顶的效果
View view2 = mInflater.inflate(R.layout.avatar, null);
ImageView iv = (ImageView) view2.findViewById(R.id.imv_pic);
TextView tv = (TextView) view2.findViewById(R.id.tv_num);
tv.setText("第" + (i) + "个");
if (i % 2 == 0) {
tv.setTextColor(getResources().getColor(R.color.white));
} else {
tv.setTextColor(getResources().getColor(R.color.black));
}
if (ChooseIndex == i) {
Glide.with(mContext).load(R.mipmap.anniu115).into(iv);
tv.setTextColor(getResources().getColor(R.color.white));
} else {
Glide.with(mContext).load(pic[i % 2]).into(iv);
}
this.addView(view2);
}
}
@Override
public void onClick(View view) {
int count = getChildCount();
for (int i = 0; i < count; i++) {
if (getChildAt(i).equals(view)) {
Log.i("DiscussionAvatarView==", "点击的是第" + (count - i - 1) + "个");
ChooseIndex = count - i - 1;
mOnItemClickListener.onViewClick(ChooseIndex);
}
}
initDatas();
}
/**
* 点击事件
*/
public interface OnItemViewClickListener {
/**
* 重试按钮点击
*/
void onViewClick(int poi);
}
/**
* 设置重试点击事件
*
* @param l 重试的点击事件
*/
public void setOnItemViewClickListener(OnItemViewClickListener l) {
mOnItemClickListener = l;
}
}
- 调用方法
由于要实现水平滚动,所以结合了HorizontalScrollView
使用
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<HorizontalScrollView
android:id="@+id/scrollview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scrollbars="none"
android:layout_gravity="left">
<com.light.mytext.mycustomview.DiscussionAvatarView
android:id="@+id/daview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:isLastComplete="true"
app:isShowAnimation="false"
app:isShowFrame="false"
app:iviewHeight="48"
app:iviewWidth="120"
app:maxCount="6"
app:space="0.8"
></com.light.mytext.mycustomview.DiscussionAvatarView>
</HorizontalScrollView>
</LinearLayout>
Java代码实现:
package com.light.mytext.mycustomview
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.light.mytext.R
import com.light.mytext.utiles.ToastUtils
import kotlinx.android.synthetic.main.activity_my_pin_jie.*
import java.util.*
class MyPinJieActivity : AppCompatActivity() {
private val mDatas = ArrayList<String>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_my_pin_jie)
initTestDatas()
daview.initDatasList(mDatas)
//自动滚动到最后一个item
/* Timer timer=new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
scrollview.fullScroll(HorizontalScrollView.FOCUS_RIGHT);
}
},1000);*/
daview.setOnItemViewClickListener {
poi ->
ToastUtils.show(
this@MyPinJieActivity,
"点击的是第" + poi + "个"
)
}
}
private fun initTestDatas() {
for (i in 0..7) {
mDatas.add("")
}
}
}
- 资源文件
attrs.xml
<declare-styleable name="DiscussionAvatarView">
<!--头像的半径,dp为单位-->
<attr name="iviewWidth" format="reference|integer" />
<attr name="iviewHeight" format="reference|integer" />
<!--头像间的距离,为头像直径的长度的百分比,dp为单位-->
<attr name="space" format="reference|float" />
<!--最多显示多少个头像-->
<attr name="maxCount" format="reference|integer" />
<!--是否最后一个显示完全,默认是true-->
<attr name="isLastComplete" format="reference|boolean" />
<!--是否显示动画效果-->
<attr name="isShowAnimation" format="reference|boolean" />
<!--是否显示边框-->
<attr name="isShowFrame" format="reference|boolean" />
<!--边框颜色-->
<attr name="frameColor" format="reference|color" />
</declare-styleable>
avatar.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/imv_pic"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@mipmap/anniu5"
android:scaleType="fitXY"></ImageView>
<TextView
android:id="@+id/tv_num"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="1"
android:textColor="#000000"
android:layout_centerInParent="true"
android:textSize="15sp" />
</RelativeLayout>