详解android使用ItemDecoration 悬浮导航栏效果

开始逐渐领略到ItemDecoration的美~

今天让我 使用 ItemDecoration 来完成 可推动的悬浮导航栏的效果,最终实现的效果如下图:

具体实现步骤如下:

根据我前面的文章所讲的RecyclerView的基本使用,我们先来完成基本的recyclerView:

第一步:布局里写一个RecyclerView

第二步:实例化

recyclerView = (RecyclerView) findViewById(R.id.recyclerView);

第三步:获取所需的数据 (这里我们来个真实点的情景,去联网请求数据)

/**
  * 联网请求所需的url
  */
  public String url="http://api.meituan.com/mmdb/movie/v2/list/rt/order/coming.json?ci=1&limit=12&token=&__vhost=api.maoyan.com&utm_campaign=AmovieBmovieCD-1&movieBundleVersion=6801&utm_source=xiaomi&utm_medium=android&utm_term=6.8.0&utm_content=868030022327462&net=255&dModel=MI%205&uuid=0894DE03C76F6045D55977B6D4E32B7F3C6AAB02F9CEA042987B380EC5687C43&lat=40.100673&lng=116.378619&__skck=6a375bce8c66a0dc293860dfa83833ef&__skts=1463704714271&__skua=7e01cf8dd30a179800a7a93979b430b2&__skno=1a0b4a9b-44ec-42fc-b110-ead68bcc2824&__skcy=sXcDKbGi20CGXQPPZvhCU3%2FkzdE%3D";
//联网获取数据
    getDataFromNet();
/**
   * 使用okhttpUtils进行联网请求数据
   */
  private void getDataFromNet() {
    OkHttpUtils.
        get()
        .url(url)
        .build()
        .execute(new StringCallback() {
          @Override
          public void onError(okhttp3.Call call, Exception e, int id) {
            Log.e("TAG", "联网失败" + e.getMessage());
          } 

          @Override
          public void onResponse(String response, int id) {
            Log.e("TAG", "联网成功==" + response); 

            //联网成功后使用fastjson解析
            processData(response);
          }
        });
  }
/**
   * 使用fastjson进行解析
   *
   * @param json
   */
  private void processData(String json) {
    //这里使用GsonFormat生成对应的bean类
    JSONObject jsonObject = parseObject(json); 

    String data = jsonObject.getString("data");
    JSONObject dataObj = JSON.parseObject(data); 

    String coming = dataObj.getString("coming");
    List<WaitMVBean.DataBean.ComingBean> comingslist = parseArray(coming, WaitMVBean.DataBean.ComingBean.class); 

    //测试是否解析数据成功
//    String strTest = comingslist.get(0).getCat();
//    Log.e("TAG", strTest + "222"); 

     //解析数据成功,设置适配器--> 

    } 

  }

第四步:解析数据成功后,创建并设置适配器,并传递相关数据

//解析数据成功,设置适配器
      MyRecyclerAdapter adapter = new MyRecyclerAdapter( mContext,comingslist);
      recyclerView.setAdapter(adapter);

适配器:

public class MyRecyclerAdapter extends RecyclerView.Adapter { 

  private final List<WaitMVBean.DataBean.ComingBean> comingslist;
  private final Context mContext;
  private final LayoutInflater mLayoutInflater; 

  public MyRecyclerAdapter(Context mContext, List<WaitMVBean.DataBean.ComingBean> comingslist) {
    this.mContext = mContext;
    this.comingslist = comingslist;
    mLayoutInflater = LayoutInflater.from(mContext);
  } 

  @Override
  public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    return new MyViewHolder(mLayoutInflater.inflate(R.layout.date_item, null));
  } 

  @Override
  public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    MyViewHolder myholder = (MyViewHolder) holder;
    myholder.setData(position);
  } 

  @Override
  public int getItemCount() {
    return comingslist.size();
  } 

  class MyViewHolder extends RecyclerView.ViewHolder {
    private TextView mv_name;
    private TextView mv_dec;
    private TextView mv_date;
    private ImageView imageView; 

    public MyViewHolder(View itemView) {
      super(itemView);
      mv_name = (TextView) itemView.findViewById(R.id.mv_name);
      mv_dec = (TextView) itemView.findViewById(R.id.mv_dec);
      mv_date = (TextView) itemView.findViewById(R.id.mv_date);
      imageView = (ImageView) itemView.findViewById(R.id.image);
    } 

    public void setData(int position) {
      WaitMVBean.DataBean.ComingBean coming = comingslist.get(position); 

      String name = coming.getNm();
      mv_name.setText(name); 

      String date = coming.getShowInfo();
      mv_date.setText(date); 

      String dec = coming.getScm();
      mv_dec.setText(dec); 

      //注:当你发下图片无法打开是,做个字符串替换即可
      String imagUrl = coming.getImg();
      String newImagUrl = imagUrl.replaceAll("w.h", "50.80"); 

      //使用Glide加载图片
      Glide.with(mContext)
          .load(newImagUrl)
          .into(imageView);
    }
  }
}

item的布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:background="#ffffff"
  android:gravity="center_vertical"
  android:orientation="horizontal"> 

  <ImageView
    android:id="@+id/image"
    android:layout_width="70dp"
    android:layout_height="110dp"
    android:layout_marginBottom="5dp"
    android:layout_marginLeft="10dp"
    android:layout_marginRight="8dp"
    android:layout_marginTop="5dp" /> 

  <LinearLayout
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_marginLeft="6dp"
    android:layout_weight="1"
    android:orientation="vertical"> 

    <TextView
      android:id="@+id/mv_name"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="神奇動物在哪裏"
      android:textColor="#000000"
      android:textSize="15sp" /> 

    <LinearLayout
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:orientation="horizontal"> 

      <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="观众"
        android:textColor="#55000000"
        android:textSize="14sp" /> 

      <TextView
        android:id="@+id/tv_people"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="9.0 "
        android:textColor="#FFCE42"
        android:textSize="18sp" /> 

      <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text=" | 专业"
        android:textColor="#55000000"
        android:textSize="14sp" /> 

      <TextView
        android:id="@+id/tv_professional"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="6.7"
        android:textColor="#FFCE42"
        android:textSize="18sp" />
    </LinearLayout> 

    <TextView
      android:id="@+id/mv_dec"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_marginTop="8dp"
      android:text="神奇動物城,法師顯超能"
      android:textColor="#99000000"
      android:textSize="11sp" /> 

    <TextView
      android:id="@+id/mv_date"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_marginTop="10dp"
      android:text="今天165家影院放映2088场"
      android:textColor="#99000000"
      android:textSize="11sp" />
  </LinearLayout> 

</LinearLayout>

第五步:一定不能忘!!!

recycleView不仅要设置适配器还要设置布局管理者,否则图片不显示

GridLayoutManager manager = new GridLayoutManager(this, 1);
      recyclerView.setLayoutManager(manager);

此时RecyclerView简单的完成效果如下:

下面开始做 可推动的 悬浮导航栏:

第一步:首先我们来写一个类,它起标记的作用,来放每一个item的对应的悬浮栏的字符串

public class NameBean {
  String name; 

  public String getName() {
    return name;
  } 

  public void setName(String name) {
    this.name = name;
  }
}

第二步:自定义一个SectionDecoration 类 继承 RecyclerView的ItemDecoration

public class SectionDecoration extends RecyclerView.ItemDecoration {
  private static final String TAG = "SectionDecoration"; 

  private List<NameBean> dataList; 

  private DecorationCallback callback;
  private TextPaint textPaint;
  private Paint paint;
  private int topGap;
  private int alignBottom;
  private Paint.FontMetrics fontMetrics; 

  public SectionDecoration(List<NameBean> dataList, Context context, DecorationCallback decorationCallback) {
    Resources res = context.getResources();
    this.dataList = dataList;
    this.callback = decorationCallback;
    //设置悬浮栏的画笔---paint
    paint = new Paint();
    paint.setColor(res.getColor(R.color.colorGray)); 

    //设置悬浮栏中文本的画笔
    textPaint = new TextPaint();
    textPaint.setAntiAlias(true);
    textPaint.setTextSize(DensityUtil.dip2px(context, 14));
    textPaint.setColor(Color.DKGRAY);
    textPaint.setTextAlign(Paint.Align.LEFT);
    fontMetrics = new Paint.FontMetrics();
    //决定悬浮栏的高度等
    topGap = res.getDimensionPixelSize(R.dimen.sectioned_top);
    //决定文本的显示位置等
    alignBottom = res.getDimensionPixelSize(R.dimen.sectioned_alignBottom);
  } 

  @Override
  public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
    super.getItemOffsets(outRect, view, parent, state);
    int pos = parent.getChildAdapterPosition(view);
    Log.i(TAG, "getItemOffsets:" + pos);
    String groupId = callback.getGroupId(pos);
    if (groupId.equals("-1")) return;
    //只有是同一组的第一个才显示悬浮栏
    if (pos == 0 || isFirstInGroup(pos)) {
      outRect.top = topGap;
      if (dataList.get(pos).getName() == "") {
        outRect.top = 0;
      }
    } else {
      outRect.top = 0;
    }
  } 

  @Override
  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 view = parent.getChildAt(i);
      int position = parent.getChildAdapterPosition(view);
      String groupId = callback.getGroupId(position);
      if (groupId.equals("-1")) return;
      String textLine = callback.getGroupFirstLine(position).toUpperCase();
      if (textLine == "") {
        float top = view.getTop();
        float bottom = view.getTop();
        c.drawRect(left, top, right, bottom, paint);
        return;
      } else {
        if (position == 0 || isFirstInGroup(position)) {
          float top = view.getTop() - topGap;
          float bottom = view.getTop();
          //绘制悬浮栏
          c.drawRect(left, top - topGap, right, bottom, paint);
          //绘制文本
          c.drawText(textLine, left, bottom, textPaint);
        }
      }
    }
  } 

  @Override
  public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
    super.onDrawOver(c, parent, state);
    int itemCount = state.getItemCount();
    int childCount = parent.getChildCount();
    int left = parent.getPaddingLeft();
    int right = parent.getWidth() - parent.getPaddingRight();
    float lineHeight = textPaint.getTextSize() + fontMetrics.descent; 

    String preGroupId = "";
    String groupId = "-1";
    for (int i = 0; i < childCount; i++) {
      View view = parent.getChildAt(i);
      int position = parent.getChildAdapterPosition(view); 

      preGroupId = groupId;
      groupId = callback.getGroupId(position);
      if (groupId.equals("-1") || groupId.equals(preGroupId)) continue; 

      String textLine = callback.getGroupFirstLine(position).toUpperCase();
      if (TextUtils.isEmpty(textLine)) continue; 

      int viewBottom = view.getBottom();
      float textY = Math.max(topGap, view.getTop());
      //下一个和当前不一样移动当前
      if (position + 1 < itemCount) {
        String nextGroupId = callback.getGroupId(position + 1);
        //组内最后一个view进入了header
        if (nextGroupId != groupId && viewBottom < textY) {
          textY = viewBottom;
        }
      }
      //textY - topGap决定了悬浮栏绘制的高度和位置
      c.drawRect(left, textY - topGap, right, textY, paint);
      //left+2*alignBottom 决定了文本往左偏移的多少(加-->向左移)
      //textY-alignBottom 决定了文本往右偏移的多少 (减-->向上移)
      c.drawText(textLine, left + 2 * alignBottom, textY - alignBottom, textPaint);
    }
  } 

  /**
   * 判断是不是组中的第一个位置
   *
   * @param pos
   * @return
   */
  private boolean isFirstInGroup(int pos) {
    if (pos == 0) {
      return true;
    } else {
      // 因为是根据 字符串内容的相同与否 来判断是不是同意组的,所以此处的标记id 要是String类型
      // 如果你只是做联系人列表,悬浮框里显示的只是一个字母,则标记id直接用 int 类型就行了
      String prevGroupId = callback.getGroupId(pos - 1);
      String groupId = callback.getGroupId(pos);
      //判断前一个字符串 与 当前字符串 是否相同
      if (prevGroupId.equals(groupId)) {
        return false;
      } else {
        return true;
      }
    }
  } 

  //定义一个借口方便外界的调用
  interface DecorationCallback {
    String getGroupId(int position); 

    String getGroupFirstLine(int position);
  }
}

第三步:在向list集合中先把每一个item的 起“标记”作用的字符串都加进去

setPullAction(comingslist);
private void setPullAction(List<WaitMVBean.DataBean.ComingBean> comingslist) {
    dataList = new ArrayList<>(); 

    for (int i = 0; i < comingslist.size(); i++) {
      NameBean nameBean = new NameBean();
      String name0 = comingslist.get(i).getComingTitle();
      nameBean.setName(name0);
      dataList.add(nameBean);
    }
  }

第四步:在setAdapter() 前,为RecyclerView添加ItemDecoration:

recyclerView.addItemDecoration(new SectionDecoration(dataList,mContext, new SectionDecoration.DecorationCallback() {
        //返回标记id (即每一项对应的标志性的字符串)
        @Override
        public String getGroupId(int position) {
          if(dataList.get(position).getName()!=null) {
            return dataList.get(position).getName();
          }
          return "-1";
        } 

        //获取同组中的第一个内容
        @Override
        public String getGroupFirstLine(int position) {
          if(dataList.get(position).getName()!=null) {
            return dataList.get(position).getName();
          }
          return "";
        }
      }));

这样就完成了~

再看一眼最终效果感受一下:

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

(0)

相关推荐

  • android效果TapBarMenu绘制底部导航栏的使用方式示例

    其他的不多说了!我们来看看效果吧       一.实现方式一:直接引入compile方式 Add the dependency to your build.gradle: compile 'com.github.michaldrabik:tapbarmenu:1.0.5' 布局设计 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://sc

  • Android实现沉浸式通知栏通知栏背景颜色跟随app导航栏背景颜色而改变

    最近好多app都已经满足了沉浸式通知栏, 所谓沉浸式通知栏:就是把用来导航的各种界面操作空间隐藏在以程序内容为主的情景中,通过相对"隐形"的界面来达到把用户可视范围最大化地用到内容本身上. 而最新安卓4.4系统的通知栏沉浸模式就是在软件打开的时候通知栏和软件顶部颜色融为一体,这样不仅可以使软件和系统本身更加融为一体. 就是手机的通知栏的颜色不再是白色.黑色简单的两种了,本人用的小米4手机,米4手机中的自带软件都支持沉浸式通知栏, 举个例子:大家可以看一下自己的qq,它的标题的背景颜色是

  • Android程序开发之Fragment实现底部导航栏实例代码

    流行的应用的导航一般分为两种,一种是底部导航,一种是侧边栏. 说明 IDE:AS,Android studio; 模拟器:genymotion; 实现的效果,见下图. 具体实现 为了讲明白这个实现过程,我们贴出来的代码多一写,这样更方便理解 [最后还会放出完整的代码实现] .看上图的界面做的比较粗糙,但实现过程的骨架都具有了,想要更完美的设计,之后自行完善吧 ^0^. 布局 通过观察上述效果图,发现任意一个选项页面都有三部分组成: 顶部去除ActionBar后的标题栏: 中间一个Fragment

  • Android仿网易客户端顶部导航栏效果

    最近刚写了一个网易客户端首页导航条的动画效果,现在分享出来给大家学习学习.我说一下这个效果的核心原理.下面是效果图: 首先是布局,这个布局是我从网易客户端反编译后弄来的.大家看后应该明白,布局文件如下: <FrameLayout android:id="@id/column_navi" android:layout_width="fill_parent" android:layout_height="wrap_content" androi

  • Android开发之ListView的head消失页面导航栏的渐变出现和隐藏

    1.Fragment页面xml布局: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"

  • Android自定义View实现字母导航栏的代码

    思路分析: 1.自定义View实现字母导航栏 2.ListView实现联系人列表 3.字母导航栏滑动事件处理 4.字母导航栏与中间字母的联动 5.字母导航栏与ListView的联动 效果图: 首先,我们先甩出主布局文件,方便后面代码的说明 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/re

  • Android MarginDesign控件TabLayout导航栏使用详解

    TabLayout的使用简单介绍 比如在平常的项目中实现这样的效果,一般都是都会使用viewPageIndicate等几个开源框架直接实现,或者使用自定义的HorizontalScroll再配合ViewPage+Fragment实现.在谷歌推出marginDesign之后,实现这种效果可以直接使用TabLayout实现.另外Tablayout可以通过自定义View自定义导航栏的效果.这样使用的时候更加灵活多变. 首先需要导入design包 在app的build.gradle下添加design的包

  • Java 回调函数详解及使用

    Java 回调函数详解 前言: C语言中回调函数解释: 回调函数(Callback Function)是怎样一种函数呢? 函数是用来被调用的,我们调用函数的方法有两种: 直接调用:在函数A的函数体里通过书写函数B的函数名来调用之,使内存中对应函数B的代码得以执行.这里,A称为"主叫函数"(Caller),B称为"被叫函数"(Callee). 间接调用:在函数A的函数体里并不出现函数B的函数名,而是使用指向函数B的函数指针p来使内存中属于函数B的代码片断得以执行--听

  • Android 利用ViewPager+GridView实现首页导航栏布局分页效果

    最近我尝试使用ViewPager+GridView实现的,看起来一切正常,废话不多说,具体代码如下: 如图是效果图 首先分析下思路 1.首先是怎么布局:整体是一个ViewPager将GridView作为一个View添加到ViewPager的adapter中,下方是圆点 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.a

  • Android design包自定义tablayout的底部导航栏的实现方法

    以前做项目大多用的radiobutton,今天用tablayout来做一个tab切换页面的的效果. 实现的效果就是类似QQ.微信的页面间(也就是Fragment间)的切换.如图: 布局只要一个tablayout <android.support.design.widget.TabLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:id=&

随机推荐