安卓当下最流行的吸顶效果的实现

QNTJuliane 7年前
   <p>开始逐渐领略到 <strong>ItemDecoration</strong> 的美~</p>    <p>今天让我 使用 <strong>ItemDecoration</strong> 来完成 <strong>可推动的悬浮导航栏</strong> 的效果,最终实现的效果如下图:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/476686b398f5ad935b7aae5a9d5f092b.gif"></p>    <p>具体实现步骤如下:</p>    <p>根据我前面的文章所讲的RecyclerView的基本使用,我们 <strong>先来完成基本的recyclerView</strong> :</p>    <p>第一步:布局里写一个RecyclerView</p>    <p>第二步:实例化</p>    <pre>  <code class="language-java">recyclerView = (RecyclerView) findViewById(R.id.recyclerView);  </code></pre>    <p>第三步:获取所需的数据 (这里我们来个真实点的情景,去联网请求数据)</p>    <pre>  <code class="language-java">/**      * 联网请求所需的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⪫=40.100673&lng=116.378619&__skck=6a375bce8c66a0dc293860dfa83833ef&__skts=1463704714271&__skua=7e01cf8dd30a179800a7a93979b430b2&__skno=1a0b4a9b-44ec-42fc-b110-ead68bcc2824&__skcy=sXcDKbGi20CGXQPPZvhCU3%2FkzdE%3D";  </code></pre>    <pre>  <code class="language-java">//联网获取数据          getDataFromNet();  </code></pre>    <pre>  <code class="language-java">/**       * 使用okhttpUtils进行联网请求数据       */        private void getDataFromNet() {            OkHttpUtils.                    get()                    .url(url)                    .build()                    .execute(new StringCallback() {                        @Override                        public void onError(okhttp3.Callcall, Exception e, int id) {                            Log.e("TAG", "联网失败" + e.getMessage());                        }                            @Override                        public void onResponse(String response, int id) {                            Log.e("TAG", "联网成功==" + response);                                //联网成功后使用fastjson解析                            processData(response);                        }                    });        }  </code></pre>    <pre>  <code class="language-java">/**       * 使用fastjson进行解析       *       * @param json       */        private void processData(String json) {            //这里使用GsonFormat生成对应的bean类          JSONObjectjsonObject = parseObject(json);                String data = jsonObject.getString("data");            JSONObjectdataObj = 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");                //解析数据成功,设置适配器-->                      }            }  </code></pre>    <p>第四步:解析数据成功后,创建并设置适配器,并传递相关数据</p>    <pre>  <code class="language-java">//解析数据成功,设置适配器              MyRecyclerAdapteradapter = new MyRecyclerAdapter( mContext,comingslist);              recyclerView.setAdapter(adapter);  </code></pre>    <p>适配器:</p>    <pre>  <code class="language-java">public class MyRecyclerAdapterextends RecyclerView.Adapter {            private final List<WaitMVBean.DataBean.ComingBean> comingslist;        private final ContextmContext;        private final LayoutInflatermLayoutInflater;                public MyRecyclerAdapter(ContextmContext, List<WaitMVBean.DataBean.ComingBean> comingslist) {            this.mContext = mContext;            this.comingslist = comingslist;            mLayoutInflater = LayoutInflater.from(mContext);        }            @Override        public RecyclerView.ViewHolderonCreateViewHolder(ViewGroupparent, int viewType) {            return new MyViewHolder(mLayoutInflater.inflate(R.layout.date_item, null));        }            @Override        public void onBindViewHolder(RecyclerView.ViewHolderholder, int position) {            MyViewHoldermyholder = (MyViewHolder) holder;            myholder.setData(position);        }            @Override        public int getItemCount() {            return comingslist.size();        }            class MyViewHolderextends RecyclerView.ViewHolder {            private TextViewmv_name;            private TextViewmv_dec;            private TextViewmv_date;            private ImageViewimageView;                public MyViewHolder(ViewitemView) {                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.ComingBeancoming = 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);            }        }    }  </code></pre>    <p>item的布局:</p>    <pre>  <code class="language-java"><?xmlversion="1.0" encoding="utf-8"?>    <LinearLayoutxmlns: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>  </code></pre>    <p>第五步:一定不能忘!!!</p>    <p>recycleView不仅要设置适配器还要设置布局管理者,否则图片不显示</p>    <pre>  <code class="language-java">GridLayoutManagermanager = new GridLayoutManager(this, 1);                recyclerView.setLayoutManager(manager);  </code></pre>    <p>此时RecyclerView简单的完成效果如下:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/8f7872f2724cb84146bc6edad9e450f6.gif"></p>    <p>下面开始做 可推动的 悬浮导航栏:</p>    <p>第一步:首先我们来写一个类,它起标记的作用,来放每一个item的对应的悬浮栏的字符串</p>    <pre>  <code class="language-java">public class NameBean {        String name;            public String getName() {            return name;        }            public void setName(String name) {            this.name = name;        }    }  </code></pre>    <p>第二步:自定义一个 <strong>SectionDecoration 类</strong> 继承 RecyclerView的ItemDecoration</p>    <pre>  <code class="language-java">public class SectionDecorationextends RecyclerView.ItemDecoration {        private static final String TAG = "SectionDecoration";            private List<NameBean> dataList;            private DecorationCallbackcallback;        private TextPainttextPaint;        private Paintpaint;        private int topGap;        private int alignBottom;        private Paint.FontMetricsfontMetrics;                public SectionDecoration(List<NameBean> dataList, Contextcontext, DecorationCallbackdecorationCallback) {            Resourcesres = 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(RectoutRect, Viewview, RecyclerViewparent, RecyclerView.Statestate) {            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, RecyclerViewparent, RecyclerView.Statestate) {            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++) {                Viewview = 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, RecyclerViewparent, RecyclerView.Statestate) {            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++) {                Viewview = 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);        }    }  </code></pre>    <p>第三步:在向list集合中先把每一个item的 起“标记”作用的字符串都加进去</p>    <pre>  <code class="language-java">setPullAction(comingslist);  </code></pre>    <pre>  <code class="language-java">private void setPullAction(List<WaitMVBean.DataBean.ComingBean> comingslist) {            dataList = new ArrayList<>();                for (int i = 0; i < comingslist.size(); i++) {                NameBeannameBean = new NameBean();                String name0 = comingslist.get(i).getComingTitle();                nameBean.setName(name0);                dataList.add(nameBean);            }        }  </code></pre>    <p>第四步:在setAdapter() 前,为RecyclerView添加ItemDecoration:</p>    <pre>  <code class="language-java">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 "";                    }                }));  </code></pre>    <p>这样就 <strong>完成</strong> 了~</p>    <p>再看一眼最终效果感受一下:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/476686b398f5ad935b7aae5a9d5f092b.gif"></p>    <p> </p>    <p>来自:http://android.jobbole.com/85325/</p>    <p> </p>