Android ItemDecoration 实现分组索引列表的示例代码

本文介绍了Android ItemDecoration 实现分组索引列表的示例代码,分享给大家。具体如下:

先来看看效果:

我们要实现的效果主要涉及三个部分:

  1. 分组 GroupHeader
  2. 分割线
  3. SideBar

前两个部分涉及到一个ItemDecoration类,也是我们接下来的重点,该类是RecyclerView的一个抽象静态内部类,主要作用就是给RecyclerView的ItemView绘制额外的装饰效果,例如给RecyclerView添加分割线。

使用ItemDecoration时需要继承该类,根据需求可以重写如下三个方法,其它的方法已经deprecated了:

public class GroupHeaderItemDecoration extends RecyclerView.ItemDecoration {
  @Override
  public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
    super.getItemOffsets(outRect, view, parent, state);
  }

  @Override
  public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
    super.onDraw(c, parent, state);
  }

  @Override
  public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
    super.onDrawOver(c, parent, state);
  }
}

然后将其添加到RecyclerView中:

recyclerView.addItemDecoration(new GroupHeaderItemDecoration())

了解这个三个方法的作用,这样才能更好的实现我们想要的功能:

1、getItemOffsets()

给指定的ItemView设置偏移量,具体怎么设置呢,咱们看图说话:

图中左边的是原始RecyclerView列表,右边是设置了ItemView偏移量的列表,其实相当于在ItemView外部添加了一个矩形区域
其中left、top、right、bottom就是ItemView在四个方向的偏移量,对应的设置代码如下:

outRect.set(left, top, right, bottom)

在我们的分组索引列表中,只需要对ItemView设置顶部的偏移量,其它三个偏移量为0即可。这样就可以在ItemView顶部预留出一定高度的区域,如下图:

2、onDraw()

在getItemOffsets()方法中,我们设置了偏移量,进而得到了对应的偏移区域,接下来在onDraw()中就可以给ItemView绘制装饰效果了,所以我们在该方法中将分组索引列表中的GroupHeader的内容绘制在ItemView顶部偏移区域里。也就是绘制前边 gif 图里的A、B、C... GroupHeader,虽然看起来像一个个独立的ItemView,但并不是的哦!

注意该绘制操作会在ItemView的onDraw()前完成的!

3、onDrawOver()

该方法同样也是用来绘制的,但是它在ItemDecoration的onDraw()方法和ItemView的onDraw()完成后才执行。所以其绘制的内容会遮挡在RecyclerView上,因此我们可以在该方法中绘制分组索引列表中悬浮的GroupHeader,也就是在列表顶部随着列表滚动切换的GroupHeader。

一、分组GroupHeader

三个方法的作用已经解释完了,接下来就是代码实现我们的效果了:

首先保证RecyclerView的数据源已经按照某种规律进行了分组排序,具体什么规律你说了算,我们例子中按照数据源中指定字段的值的首字母升序排列,也就是常见通讯录的排序方式。然后在每个data中保存需要在GroupHeader上显示的内容,可以使用tag字段,我们这里保存的是对应的首字母。这里没必要将整个数据源设置到ItemDecoration里边,所以我们只需要提取排序后数据源的tag保存到列表中,然后设置到ItemDecoration里边,后边的操作就依赖设置的数据源了,根据tag的异同来决定是否绘制GroupHeader等。

上边已经分析了,GroupHeader只在列表中每组数据对应的第一个ItemView顶部显示,只需要对ItemView设置顶部的偏移量即可:

public class GroupHeaderItemDecoration extends RecyclerView.ItemDecoration {
  @Override
  public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
    super.getItemOffsets(outRect, view, parent, state);
    RecyclerView.LayoutManager manager = parent.getLayoutManager();

    //只处理线性垂直类型的列表
    if ((manager instanceof LinearLayoutManager)
        && LinearLayoutManager.VERTICAL != ((LinearLayoutManager) manager).getOrientation()) {
      return;
    }

    int position = parent.getChildAdapterPosition(view);
    //ItemView的position==0 或者 当前ItemView的data的tag和上一个ItemView的不相等,则为当前ItemView设置top 偏移量
    if (!Utils.listIsEmpty(tags) && (position == 0 || !tags.get(position).equals(tags.get(position - 1)))) {
      outRect.set(0, groupHeaderHeight, 0, 0);
    }
  }

  @Override
  public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
    super.onDraw(c, parent, state);
  }

  @Override
  public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
    super.onDrawOver(c, parent, state);
  }
}

其中tags就是我们设置到ItemDecoration的数据源,是一个String集合。groupHeaderHeight就是ItemView的顶部偏移量。

之后就是在ItemView的顶部偏移区域绘制GroupHeader了:

public class GroupHeaderItemDecoration extends RecyclerView.ItemDecoration {
  @Override
  public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
    super.getItemOffsets(outRect, view, parent, state);
  }

  @Override
  public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
    super.onDraw(c, parent, state);
    for (int i = 0; i < parent.getChildCount(); i++) {
      View view = parent.getChildAt(i);
      int position = parent.getChildAdapterPosition(view);
      String tag = tags.get(position);
      //和getItemOffsets()里的条件判断类似,开始绘制分组的GroupHeader
      if (!Utils.listIsEmpty(tags) && (position == 0 || !tag.equals(tags.get(position - 1)))) {
        drawGroupHeader(c, parent, view, tag);
      }
    }
  }

  @Override
  public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
    super.onDrawOver(c, parent, state);
  }

  private void drawGroupHeader(Canvas c, RecyclerView parent, View view, String tag) {
    RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();
    int left = parent.getPaddingLeft();
    int right = parent.getWidth() - parent.getPaddingRight();
    int bottom = view.getTop() - params.topMargin;
    int top = bottom - groupHeaderHeight;
    c.drawRect(left, top, right, bottom, mPaint);
    int x = left + groupHeaderLeftPadding;
    int y = top + (groupHeaderHeight + Utils.getTextHeight(mTextPaint, tag)) / 2;
    c.drawText(tag, x, y, mTextPaint);
  }
}

绘制GroupHeader就是Canvasc操作,先绘制一个矩形框,再绘制相应的文字,当然绘制图片也是没问题的,其中groupHeaderLeftPadding是个可配置字段,代表绘制的文字或图片到列表左边沿的距离,也可以理解为GroupHeader的左padding。

最后就是悬浮在顶部的GroupHeader绘制了:

public class GroupHeaderItemDecoration extends RecyclerView.ItemDecoration {
  @Override
  public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
    super.getItemOffsets(outRect, view, parent, state);
  }

  @Override
  public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
    super.onDraw(c, parent, state);
  }

  @Override
  public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
    super.onDrawOver(c, parent, state);
    if (!show) {
      return;
    }
    //列表第一个可见的ItemView位置
    int position = ((LinearLayoutManager) (parent.getLayoutManager())).findFirstVisibleItemPosition();
    String tag = tags.get(position);
    View view = parent.findViewHolderForAdapterPosition(position).itemView;
    //当前ItemView的data的tag和下一个itemView的不相等,则代表将要重新绘制悬停的GroupHeader
    boolean flag = false;
    if (!Utils.listIsEmpty(tags) && (position + 1) < tags.size() && !tag.equals(tags.get(position + 1))) {
      //如果第一个可见ItemView的底部坐标小于groupHeaderHeight,则执行Canvas向上位移操作
      if (view.getBottom() <= groupHeaderHeight) {
        c.save();
        flag = true;
        c.translate(0, view.getHeight() + view.getTop() - groupHeaderHeight);
      }
    }

    drawSuspensionGroupHeader(c, parent, tag);

    if (flag) {
      c.restore();
    }
  }

  private void drawSuspensionGroupHeader(Canvas c, RecyclerView parent, String tag) {
    int left = parent.getPaddingLeft();
    int right = parent.getWidth() - parent.getPaddingRight();
    int bottom = groupHeaderHeight;
    int top = 0;
    c.drawRect(left, top, right, bottom, mPaint);
    int x = left + groupHeaderLeftPadding;
    int y = top + (groupHeaderHeight + Utils.getTextHeight(mTextPaint, tag)) / 2;
    c.drawText(tag, x, y, mTextPaint);
  }
}

绘制操作和onDraw中的类似,gif 中有一个悬浮GroupHeader上移的动画,就是通过Canvas位移来实现的,注意在Canvas位移的前后进行save()和restore()操作。

我们给GroupHeaderItemDecoration提供了设置GroupHeader左padding、高度、背景色、文字颜色、尺寸、以及是否显示顶部悬浮GroupHeader的方法,方便使用。

关于绘制操作需要注意的是,GroupHeader所在的偏移区域和ItemView是相互独立的,不要把GroupHeader当做ItemView的一部分哦。到这里GroupHeader的功能就实现了,只需要将GroupHeaderItemDecoration添加到RecyclerView即可。

至于如何通过layout或者View来实现GroupHeader,做过一些尝试,效果都不理想,期待大家的好想法哦!

这里先用一个接口,对外提供自定义绘制GroupHeader的方法:

public interface OnDrawItemDecorationListener {
  /**
   * 绘制GroupHeader
   * @param c
   * @param paint 绘制GroupHeader区域的paint
   * @param textPaint 绘制文字的paint
   * @param params  共四个值left、top、right、bottom 代表GroupHeader所在区域的四个坐标值
   * @param position 原始数据源中的position
   */
  void onDrawGroupHeader(Canvas c, Paint paint, TextPaint textPaint, int[] params, int position);
   /**
   * 绘制悬浮在列表顶部的GroupHeader
   */
  void onDrawSuspensionGroupHeader(Canvas c, Paint paint, TextPaint textPaint, int[] params, int position);
}

二、分割线

现在RecyclerView还差一个分割线,当前最笨的办法可以在ItemView的布局文件中设置,既然系统都提供了ItemDecoration,那用它来优雅的实现为何不可呢,我们只需要给列表中每组数据除了最后一项数据对应的ItemView之外的添加分割线即可,也就是不给每组数据对应的最后一个ItemView添加分割线。很简单,直接上核心代码:

public class DivideItemDecoration extends RecyclerView.ItemDecoration {
  @Override
  public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
    super.getItemOffsets(outRect, view, parent, state);
    RecyclerView.LayoutManager manager = parent.getLayoutManager();

    //只处理线性垂直类型的列表
    if ((manager instanceof LinearLayoutManager)
        && LinearLayoutManager.VERTICAL != ((LinearLayoutManager) manager).getOrientation()) {
      return;
    }

    int position = parent.getChildAdapterPosition(view);
    if (!Utils.listIsEmpty(tags) && (position + 1) < tags.size() && tags.get(position).equals(tags.get(position + 1))) {
      //当前ItemView的data的tag和下一个ItemView的不相等,则为当前ItemView设置bottom 偏移量
      outRect.set(0, 0, 0, divideHeight);
    }
  }

  @Override
  public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
    super.onDraw(c, parent, state);
    for (int i = 0; i < parent.getChildCount(); i++) {
      View view = parent.getChildAt(i);
      int position = parent.getChildAdapterPosition(view);
      //和getItemOffsets()里的条件判断类似
      if (!Utils.listIsEmpty(tags) && (position + 1) < tags.size() && tags.get(position).equals(tags.get(position + 1))) {
        drawDivide(c, parent, view);
      }
    }
  }

  @Override
  public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
    super.onDrawOver(c, parent, state);
  }

  private void drawDivide(Canvas c, RecyclerView parent, View view) {
    RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();
    int left = parent.getPaddingLeft();
    int right = parent.getWidth();
    int top = view.getBottom() + params.bottomMargin;
    int bottom = top + divideHeight;
    c.drawRect(left, top, right, bottom, mPaint);
  }
}

三、SideBar

SideBar就是 gif 图右边的垂直字符条,是一个自定义View。手指触摸选中一个字符,则列表会滚动到对应的分组头部位置。实现起来也蛮简单的,核心代码如下:

public class SideBar extends View {
  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    int widthSize = MeasureSpec.getSize(widthMeasureSpec);
    int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    int heightSize = MeasureSpec.getSize(heightMeasureSpec);
    int heightMode = MeasureSpec.getMode(heightMeasureSpec);

    //重新计算SideBar宽高
    if (heightMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.AT_MOST) {
      getMaxTextSize();
      if (heightMode == MeasureSpec.AT_MOST) {
        heightSize = (maxHeight + 15) * indexArray.length;
      }

      if (widthMode == MeasureSpec.AT_MOST) {
        widthSize = maxWidth + 10;
      }
    }

    setMeasuredDimension(widthSize, heightSize);
  }

  @Override
  protected void onDraw(Canvas canvas) {
    for (int i = 0; i < indexArray.length; i++) {
      String index = indexArray[i];
      float x = (mWidth - mTextPaint.measureText(index)) / 2;
      float y = mMarginTop + mHeight * i + (mHeight + Utils.getTextHeight(mTextPaint, index)) / 2;
      //绘制字符
      canvas.drawText(index, x, y, mTextPaint);
    }
  }

  @Override
  public boolean onTouchEvent(MotionEvent event) {
    switch (event.getAction()) {
      case MotionEvent.ACTION_DOWN:
      case MotionEvent.ACTION_MOVE:
        // 选中字符的下标
        int pos = (int) ((event.getY() - mMarginTop) / mHeight);
        if (pos >= 0 && pos < indexArray.length) {
          setBackgroundColor(TOUCH_COLOR);
          if (onSideBarTouchListener != null) {
            for (int i = 0; i < tags.size(); i++) {
              if (indexArray[pos].equals(tags.get(i))) {
                onSideBarTouchListener.onTouch(indexArray[pos], i);
                break;
              } else {
                onSideBarTouchListener.onTouch(indexArray[pos], -1);
              }
            }
          }
        }
        break;
      case MotionEvent.ACTION_UP:
      case MotionEvent.ACTION_CANCEL:
        setBackgroundColor(UNTOUCH_COLOR);
        if (onSideBarTouchListener != null) {
          onSideBarTouchListener.onTouchEnd();
        }
        break;
    }

    return true;
  }
}

在onMeasure()方法里,如果SideBar的宽、高测量模式为MeasureSpec.AT_MOST则重新计算SideBar的宽、高。onDraw()方法则是遍历索引数组,并绘制字符索引。在onTouchEvent()方法里,我们根据手指在SideBar上触摸坐标点的y值,计算出触摸的相应字符,以便在OnSideBarTouchListener接口进行后续操作,例如列表的跟随滚动等等。

四、实例

前边已经完成了三大核心功能,最后来愉快的使用下吧:

public class MainActivity extends AppCompatActivity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    RecyclerView recyclerView = (RecyclerView) findViewById(R.id.list);
    SideBar sideBar = (SideBar) findViewById(R.id.side_bar);
    final TextView tip = (TextView) findViewById(R.id.tip);

    final List<ItemData> datas = new ArrayList<>();
    ItemData data = new ItemData("北京");
    datas.add(data);
    ItemData data1 = new ItemData("上海");
    datas.add(data1);
    ItemData data2 = new ItemData("广州");
    datas.add(data2);
    .
    .
    .
    ItemData data34 = new ItemData("Hello China");
    datas.add(data34);
    ItemData data35 = new ItemData("宁波");
    datas.add(data35);

    SortHelper<ItemData> sortHelper = new SortHelper<ItemData>() {
      @Override
      public String sortField(ItemData data) {
        return data.getTitle();
      }
    };
    sortHelper.sortByLetter(datas);//将数据源按指定字段首字母排序
    List<String> tags = sortHelper.getTags(datas);//提取已排序数据源的tag值

    MyAdapter adapter = new MyAdapter(this, datas, false);
    final LinearLayoutManager layoutManager = new LinearLayoutManager(this);
    layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
    recyclerView.setLayoutManager(layoutManager);
    //添加分割线
    recyclerView.addItemDecoration(new DivideItemDecoration().setTags(tags));
    //添加GroupHeader
    recyclerView.addItemDecoration(new GroupHeaderItemDecoration(this)
        .setTags(tags)//设置tag集合
        .setGroupHeaderHeight(30)//设置GroupHeader高度
        .setGroupHeaderLeftPadding(20));//设置GroupHeader 左padding
    recyclerView.setAdapter(adapter);

    sideBar.setOnSideBarTouchListener(tags, new OnSideBarTouchListener() {
      @Override
      public void onTouch(String text, int position) {
        tip.setVisibility(View.VISIBLE);
        tip.setText(text);
        if ("↑".equals(text)) {
          layoutManager.scrollToPositionWithOffset(0, 0);
          return;
        }
        //滚动列表到指定位置
        if (position != -1) {
          layoutManager.scrollToPositionWithOffset(position, 0);
        }
      }

      @Override
      public void onTouchEnd() {
        tip.setVisibility(View.GONE);
      }
    });
  }
}

这也就是文章开头的 gif 效果。如果需要自定义ItemView的绘制可以这样写:

recyclerView.addItemDecoration(new GroupHeaderItemDecoration(this)
        .setTags(tags)
        .setGroupHeaderHeight(30)
        .setGroupHeaderLeftPadding(20)
        .setOnDrawItemDecorationListener(new OnDrawItemDecorationListener() {
          @Override
          public void onDrawGroupHeader(Canvas c, Paint paint, TextPaint textPaint, int[] params, int position) {
            c.drawRect(params[0], params[1], params[2], params[3], paint);

            int x = params[0] + Utils.dip2px(context, 20);
            int y = params[1] + (Utils.dip2px(context, 30) + Utils.getTextHeight(textPaint, tags.get(position))) / 2;

            Bitmap icon = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher, null);
            Bitmap icon1 = Bitmap.createScaledBitmap(icon, Utils.dip2px(context, 20), Utils.dip2px(context, 20), true);
            c.drawBitmap(icon1, x, params[1] + Utils.dip2px(context, 5), paint);

            c.drawText(tags.get(position), x + Utils.dip2px(context, 25), y, textPaint);
          }

          @Override
          public void onDrawSuspensionGroupHeader(Canvas c, Paint paint, TextPaint textPaint, int[] params, int position) {
            c.drawRect(params[0], params[1], params[2], params[3], paint);
            int x = params[0] + Utils.dip2px(context, 20);
            int y = params[1] + (Utils.dip2px(context, 30) + Utils.getTextHeight(textPaint, tags.get(position))) / 2;

            Bitmap icon = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher, null);
            Bitmap icon1 = Bitmap.createScaledBitmap(icon, Utils.dip2px(context, 20), Utils.dip2px(context, 20), true);
            c.drawBitmap(icon1, x, params[1] + Utils.dip2px(context, 5), paint);

            c.drawText(tags.get(position), x + Utils.dip2px(context, 25), y, textPaint);
          }
        })
    );

坐标计算有点复杂了......0_o......

看下效果:

当然不止于此,更多的效果等待着机智的你去创造。

更多代码细节及用法可参考:https://github.com/Othershe/GroupIndexLib

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android通用索引栏实现代码

    偶尔看到之前写过的代码,感觉好多东西几乎在很多项目中都要用到,虽然每个项目的需求和设计都不同,不过实现的效果都是一样的,可能只是数据格式和一些颜色等的细微差距.但是有的时候因为一个小改变,就要去重复的修改代码,麻烦不说,也容易导致新的问题和BUG. 就拿忽然想到的索引栏来说,几乎写过的项目中都用到了,比如城市选择.联系人等等.这些地方全都需要用到索引栏,但是用法都是一样的.翻看了几处之前写过的代码,发现每次用到索引栏,都要重新去写方法来处理数据或者对数据的索引进行提取这些,做法也都大同小异.于是

  • Android手机联系人快速索引(手机通讯录)

    最近需要实现一个手机通讯录的快速索引功能.根据姓名首字母快速索引功能.下面是一个手机联系人快速索引的效果,总体来说代码不算难,拼音转换的地方略有复杂.下面上源码:源码中有注释. 下面是效果图: MainActivity: import java.util.ArrayList; import java.util.Collections; import java.util.List; import android.app.Activity; import android.os.Bundle; imp

  • android将搜索引擎设置为中国雅虎无法搜索问题解决方法

    该问题是由于yahoo的搜索接口改变导致,请修改 Donottranslate-all_search_engines.xml (x:\6575gb2\v2.12\alps\mediatek\source\frameworks\banyan\res\res\values)41753 8/11/2011 中的<string-array name="yahoo_cn" translatable="false">的定义为 复制代码 代码如下: <strin

  • Android自定义View实现通讯录字母索引(仿微信通讯录)

    一.效果:我们看到很多软件的通讯录在右侧都有一个字母索引功能,像微信,小米通讯录,QQ,还有美团选择地区等等.这里我截了一张美团选择城市的图片来看看: 我们今天就来实现图片中右侧模块的索引功能,包括触摸显示以选中的索引字母.这里我的UI界面主要是参照微信的界面来实现,所以各位也可以对照微信来看看效果,什么都不说了,只有效果图最具有说服力! 二.分析: 我们看到这样的效果我们心理都回去琢磨,他是如何实现的: 首先,它肯定是通过自定义 View 来实现的,因为 Android 没有提供类似这样的控件

  • Android手机联系人带字母索引的快速查找

    喜欢另辟蹊径的我,在这里废话不多说了,直接上代码和图片了. 效果图如下: 第一步:MainActivity的代码如下: package net.loonggg.test; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.TreeSet; import android.os.Bundle; import and

  • Android 实现带字母索引的侧边栏功能

    之前已经用自定义View做出如下这样一个效果了 这两天需要重新拿来使用,发现效果虽然做出来了,不过思路不太对,就重新参考写了一个,用法也更为简单了 首要的自然是需要继承View绘制出侧边栏,并向外提供一个监听字母索引变化的方法 /** * 作者:叶应是叶 * 时间:2017/8/20 11:38 * 描述: */ public class LetterIndexView extends View { public interface OnTouchingLetterChangedListener

  • android 左右滑动+索引图标实现方法与代码

    使用Gallery和ImageView实现android左右滑动+索引图标效果. 首先自定义Gallery实现一次只能滑动一个页面 复制代码 代码如下: public class MGalleryView extends Gallery{ public MGalleryView(Context context, AttributeSet attrs) { super(context, attrs); } //一次只能滑动一张图片注:一张图充满全屏 @Override public boolean

  • android仿微信通讯录搜索示例(匹配拼音,字母,索引位置)

    前言: 仿微信通讯录搜索功能,通过汉字或拼音首字母找到匹配的联系人并显示匹配的位置 一:先看效果图 字母索引 搜索匹配 二:功能分析 1:汉字转拼音 通讯录汉字转拼音(首个字符当考虑姓氏多音字), 现在转换拼音常见的有pinyin4j和tinypinyin, pinyin4j的功能强大,包含声调多音字,tinypinyin执行快占用内存少, 如果只是简单匹配通讯录,建议使用tinypinyin,用法也很简单这里不详细介绍 拼音类 public class CNPinyin <T extends

  • Android ItemDecoration 实现分组索引列表的示例代码

    本文介绍了Android ItemDecoration 实现分组索引列表的示例代码,分享给大家.具体如下: 先来看看效果: 我们要实现的效果主要涉及三个部分: 分组 GroupHeader 分割线 SideBar 前两个部分涉及到一个ItemDecoration类,也是我们接下来的重点,该类是RecyclerView的一个抽象静态内部类,主要作用就是给RecyclerView的ItemView绘制额外的装饰效果,例如给RecyclerView添加分割线. 使用ItemDecoration时需要继

  • Android实现字母导航控件的示例代码

    目录 自定义属性 Measure测量 坐标计算 绘制 Touch事件处理 数据组装 显示效果 今天分享一个以前实现的通讯录字母导航控件,下面自定义一个类似通讯录的字母导航 View,可以知道需要自定义的几个要素,如绘制字母指示器.绘制文字.触摸监听.坐标计算等,自定义完成之后能够达到的功能如下: 完成列表数据与字母之间的相互联动; 支持布局文件属性配置; 在布局文件中能够配置相关属性,如字母颜色.字母字体大小.字母指示器颜色等属性. 主要内容如下: 自定义属性 Measure测量 坐标计算 绘制

  • Android 实现无网络传输文件的示例代码

    最近的项目需要实现一个 Android 手机之间无网络传输文件的功能,就发现了 Wifi P2P(Wifi点对点)这么一个功能,最后也实现了通过 Wifi 隔空传输文件 的功能,这里我也来整理下代码,分享给大家. Wifi P2P 是在 Android 4.0 以及更高版本系统中加入的功能,通过 Wifi P2P 可以在不连接网络的情况下,直接与配对的设备进行数据交换.相对于蓝牙,Wifi P2P 的搜索速度和传输速度更快,传输距离更远 实现的效果如下所示: 客户端.png 服务器端.png 一

  • 微信小程序拖拽排序列表的示例代码

    拖拽排序列表 思路 界面分为两层: 底层,正常列表展示,拖拽的时候不做处理(大牛直接加了动画,原谅我技艺不精,还没实现) 顶层,movable-view组件,不长按不展示,之后长按才展示,且没有点击事件. 事件 主要监听:longpress , touchmove , touchend 三个事件 longpress 保障长按才有效,并设定许多其他值. touchmove 滑动的时候触发 判断是否需要滑动页面,因为 movable-area组件 滑动事件被catch掉,无法滑动: 记录滑动经过的项

  • 利用React实现虚拟列表的示例代码

    目录 列表项高度固定 代码实现 列表项高度动态 代码实现 思路说明 一些需要注意的问题 结尾 大家好,我是前端西瓜哥.这次我们来看看虚拟列表是什么玩意,并用 React 来实现两种虚拟列表组件. 虚拟列表,其实就是将一个原本需要全部列表项的渲染的长列表,改为只渲染可视区域内的列表项,但滚动效果还是要和渲染所有列表项的长列表一样. 虚拟列表解决的长列表渲染大量节点导致的性能问题: 一次性渲染大量节点,会占用大量 GPU 资源,导致卡顿: 即使渲染好了,大量的节点也持续占用内存.列表项下的节点越多,

  • Android串口通信封装之OkUSB的示例代码

    本文介绍了Android串口通信封装之OkUSB的示例代码,分享给大家.具体如下: Github传送门:OkUSB OkUSB 一个简洁的Android串口通信框架. 功能简介 支持设置波特率 支持设置数据位 支持设置停止位 支持设置校验位 支持DTS和RTS 支持串口连接状态监听 用法简介 Gradle allprojects { repositories { ... maven { url 'https://jitpack.io' } } } dependencies { compile '

  • Android 实现无网络页面切换的示例代码

    本文介绍了Android 实现无网络页面切换的示例代码,分享给大家,具体如下: 实现思路 需求是在无网络的时候显示特定的页面,想到要替换页面的地方,大多都是recyclerview或者第三方recyclerview这种需要显示数据的地方,因此决定替换掉页面中所有的recyclerview为无网络页面 实现过程 1 在BaseActivity中,当加载布局成功以后,通过id找到要替换的view,通过indexOfChild()方法,找到要替换的view的位置,再通过remove和add view来

  • Android中js和原生交互的示例代码

    本文介绍了Android中js和原生交互的示例代码,分享给大家,具体如下: 加载webview的类 public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); JavaScriptInterf

  • Android中TabLayout添加小红点的示例代码

    本文介绍了Android中TabLayout添加小红点的示例代码,分享给大家,具体如下 安卓原生的android.support.design.widget.TabLayout,配合ViewPager已经很好用了,但是有时我们会在内容更新时,在tab标题右上方加上一个红点等标记此tab内容有更新时,就需要给原生的TabLayout设置你定义的布局,用法和原生的一样,只是在代码中设置一下TabLayout的布局. 1.主布局文件 <?xml version="1.0" encodi

  • Android 快速实现状态栏透明样式的示例代码

    在手机 app 开发过程中,经常会遇到一种需求,需要将 内容区域 顶到 状态栏 中去.这个时候,下面一段代码,就能很轻松解决问题了. 上代码之前先上效果图: 下面上一段代码: getWindow().requestFeature(Window.FEATURE_NO_TITLE); if(VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { Window window = getWindow(); window.clearFlags(WindowManager.

随机推荐