1.实验环境:
Windows系统,Android Studio
2.界面分析:
?
3.界面的代码实现:
3.1分隔线的实现
每个联系人与联系人之间有着一条分隔线分隔开来
核心代码如下:
/**
* 设置屏幕的方向
* @param orientation LinearLayout布局是垂直还是水平
*/
public void setOrientation(int orientation){
if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST){
throw new IllegalArgumentException("invalid orientation");
}
mOrientation = orientation;
}
/**
* 用于绘画分隔线
* @param c 画布
* @param parent recyclerview组件
* @param state
*/
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
if (mOrientation == HORIZONTAL_LIST){
drawVerticalLine(c, parent, state);
}else {
drawHorizontalLine(c, parent, state);
}
}
/**
* 画横线, 这里的parent其实是显示在屏幕显示的这部分
* @param c 画布
* @param parent recyclerview组件
* @param state
*/
public void drawHorizontalLine(Canvas c, RecyclerView parent, RecyclerView.State state){
int left = parent.getPaddingLeft();
int right = parent.getWidth() - parent.getPaddingRight();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++){
final View child = parent.getChildAt(i);
//获得child的布局信息
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)child.getLayoutParams();
final int top = child.getBottom() + params.bottomMargin;
final int bottom = top + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
//Log.d("wnw", left + " " + top + " "+right+" "+bottom+" "+i);
}
}
/**
* 由于Divider也有长宽高,每一个Item需要向下或者向右偏移
* @param outRect 用于设置分隔线的坐标信息
* @param view
* @param parent recyclerview组件
* @param state
*/
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
if(mOrientation == HORIZONTAL_LIST){
//画横线,就是往下偏移一个分割线的高度
outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
}else {
//画竖线,就是往右偏移一个分割线的宽度
outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
}
super.getItemOffsets(outRect, view, parent, state);
if (users == null || users.size() == 0) {
return;
}
int position = ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewAdapterPosition();
if (position > users.size() - 1 || position < 0) {
return;
}
// 第一个分类不需要显示分类章节
if (position > 0 && !TextUtils.equals(users.get(position).getFirstChar(), users.get(position - 1).getFirstChar())) {
outRect.set(0, mDecorationHeight, 0, 0);
}
}
3.2stickyHeader效果
页面最上面永远是当前用户的开头字母
实现代码如下:
?
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
int left = parent.getPaddingLeft();
int right = parent.getWidth() - parent.getPaddingRight();
int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
int position = params.getViewAdapterPosition();
if (position < 0 ){
return;
}
String indexName = users.get(position).getFirstChar();
if (indexName == null) {
continue;
}
if (position > 0 && !TextUtils.equals(indexName, users.get(position - 1).getFirstChar())) {
// 背景
int top = child.getTop() - params.topMargin - mDecorationHeight;
int bottom = top + mDecorationHeight;
c.drawRect(left, top, right, bottom, mBgPaint);
// 文字
mTextPaint.getTextBounds(indexName, 0, indexName.length(), mTextBounds);
int h = mTextBounds.height();
c.drawText(indexName, left + textMarginLeft, bottom - (mDecorationHeight - h) / 2.0f, mTextPaint);
}
}
}
@Override
public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
int firstVisibleItemPosition = ((LinearLayoutManager) parent.getLayoutManager()).findFirstVisibleItemPosition();
// 第一个也不展示decoration
// if (firstVisibleItemPosition <= 0) {
// return;
// }
boolean hasTrans = false;
String indexName = users.get(firstVisibleItemPosition).getFirstChar();
// 移动decoration
if (firstVisibleItemPosition + 1 < users.size()) {
String nextIndexName = users.get(firstVisibleItemPosition + 1).getFirstChar();
if (!TextUtils.equals(indexName, nextIndexName)) {
View firstVisibleChild = parent.getChildAt(0);
int transY = firstVisibleChild.getBottom() - (parent.getPaddingTop() + mDecorationHeight);
if (transY < 0) {
c.save();
c.translate(0, transY);
hasTrans = true;
}
}
}
float left = parent.getLeft();
float top = parent.getPaddingTop();
float right = parent.getWidth() - parent.getPaddingRight();
float bottom = top + mDecorationHeight;
// 背景
c.drawRect(left, top, right, bottom, mBgPaint);
mTextPaint.getTextBounds(indexName, 0, indexName.length(), mTextBounds);
//文字
float baseline = bottom - (mDecorationHeight - mTextBounds.height()) / 2.0f;
c.drawText(indexName, parent.getPaddingLeft() + textMarginLeft, baseline, mTextPaint);
if (hasTrans) {
c.restore();
}
}
}
3.3头像功能的实现
将方形的图片转换为圆形的头像
实现代码如下:
public class CircleImageView extends androidx.appcompat.widget.AppCompatImageView {
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//由于是圆形,宽高应保持一致
int size = Math.min(getMeasuredWidth(), getMeasuredHeight());
mRadius = size / 2;
setMeasuredDimension(size, size);
}
@SuppressLint("DrawAllocation")
@Override
protected void onDraw(Canvas canvas) {
mPaint = new Paint();
Drawable drawable = getDrawable();
if (null != drawable) {
Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
//初始化BitmapShader,传入bitmap对象
BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
//计算缩放比例
mScale = (mRadius * 2.0f) / Math.min(bitmap.getHeight(), bitmap.getWidth());
Matrix matrix = new Matrix();
matrix.setScale(mScale, mScale);
bitmapShader.setLocalMatrix(matrix);
mPaint.setShader(bitmapShader);
//画圆形,指定好坐标,半径,画笔
canvas.drawCircle(mRadius, mRadius, mRadius, mPaint);
} else {
super.onDraw(canvas);
}
}
}
然后在我们设置的xml文件中添加我们创建的CircleImageView
<com.example.homework.CircleImageView
android:id="@+id/imageView"
android:layout_width="104dp"
android:layout_height="102dp"
app:srcCompat="@drawable/img1"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"/>
3.4点击事件的绑定
提示当前是什么用户
实现代码:
class MyVIewHolder extends RecyclerView.ViewHolder{
TextView textView1;
ImageView image;
//初始化控件
public MyVIewHolder(@NonNull View itemView) {
super(itemView);
textView1 = itemView.findViewById(R.id.textView2);
image = itemView.findViewById(R.id.imageView);
textView1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(context,"当前点击查看的用户为:"+textView1.getText(), Toast.LENGTH_SHORT).show();
}
});
}
}
3.5联系人的换位以及删除
用于交换不同联系人的位置以及删除某些联系人
实现代码如下:
public class Callback extends ItemTouchHelper.Callback{
@Override
public int getMovementFlags(@NonNull RecyclerView recyclerView,
@NonNull RecyclerView.ViewHolder viewHolder) {
// 设置拖动方向, 此处设置上下拖动事件
int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
// 设置滑动方向, 此处设置左右侧滑事件
int swipeFlags = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
// 应用 拖动 和 滑动 设置
return makeMovementFlags(dragFlags, swipeFlags);
}
**
* 是否启用长按拖动功能
* @return
*/
@Override
public boolean isLongPressDragEnabled() {
return true;
}
/**
* 拖动幅度设置
* 组件在宽度 / 高度 上移动超过该比例 , 就认为拖动触发, 执行拖动相关操作
* @param viewHolder
* @return
*/
@Override
public float getMoveThreshold(@NonNull RecyclerView.ViewHolder viewHolder) {
// 该案例中, 拖动操作只能上下进行
// 拖动超过条目组件高度超过 0.8 倍, 即可触发拖动操作
return 0.8f;
}
//调用设置的move移动方法
@Override
public void onMoved(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, int fromPos, @NonNull RecyclerView.ViewHolder target, int toPos, int x, int y) {
mAdapter.moveItem(viewHolder.getAdapterPosition(),target.getAdapterPosition());
}
/**
* 是否启用滑动操作
* @return 是否启用 true 启用, false 不启用
*/
@Override
public boolean isItemViewSwipeEnabled() {
return true;
}
/**
* 用户滑动距离, 设置的是比例值, 返回值为 0.5 , 就意味着滑动宽度/高度的一半, 才触发侧滑 onSwiped 方法
* @param viewHolder
* @return
*/
@Override
public float getSwipeThreshold(@NonNull RecyclerView.ViewHolder viewHolder) {
return 0.5f;
}
/**
* 滑动判定速度, 每秒移动的像素个数, 达到该速度后, 才可以被判定为滑动
* @param defaultValue
* @return
*/
@Override
public float getSwipeEscapeVelocity(float defaultValue) {
return 5000f;
}
/**
* 滑动时的回调操作
* @param viewHolder
* @param direction
*/
@Override
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
Log.i(TAG, "触发侧滑删除条目");
// 滑动指定的距离, 达到一定幅度后, 就会触发该方法回调
// 这里做的是滑动删除功能, 直接删除滑动项
// 该方法中删除指定条目, 并刷新界面
mAdapter.deleteItem(viewHolder.getAdapterPosition());
}
4.小结
本次实验通过创建继承于ItemDecoration类的MyDecoration类并且重写其中的一些方法来实现了分隔线以及StickHeader的效果,通过创建继承于ImageView类的CircleViewImage类并重写其中的方法实现了圆形头像的作用,最后通过重写CallBack类中的某些方法来实现了最后的删除以及换位的功能。通过本次实验,更加清晰的了解了RecyclerView与Adapter的作用,以及其中的方法的实现。而且通过本次对recyclerView效果的实现,熟悉了ItemDecoration类以及Callback类的某些方法,对TextView,ImageView以及LinearLayout的使用更加的娴熟。
https://gitee.com/tao-jie/MyHomework
|