zoukankan      html  css  js  c++  java
  • 通信录分组并且分组标签悬停划入划出(包含错误信息及修改)--第三方开源--PinnedSectionListView

    PinnedSectionListView在github上的链接地址是:https://github.com/beworker/pinned-section-listview 。

    下载下来后直接将PinnedSectionListView.java(在一些SDK版本拉动的时候会异常崩溃,异常信息和修改后的文档在后面)复制粘贴在要用的包中:

    activity_main.xml:

     1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     2     xmlns:tools="http://schemas.android.com/tools"
     3     android:layout_width="match_parent"
     4     android:layout_height="match_parent"
     5     tools:context="com.zzw.testpinnedsectionlistview.MainActivity" >
     6 
     7     <com.zzw.testpinnedsectionlistview.PinnedSectionListView
     8         android:id="@+id/listView"
     9         android:layout_width="match_parent"
    10         android:layout_height="match_parent" />
    11 
    12 </RelativeLayout>

    item.xml:

     1 <?xml version="1.0" encoding="utf-8"?>
     2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     3     android:layout_width="match_parent"
     4     android:layout_height="match_parent"
     5     android:orientation="vertical" >
     6 
     7     <ImageView
     8         android:id="@+id/imageView"
     9         android:layout_width="wrap_content"
    10         android:layout_height="wrap_content"
    11         android:layout_alignParentLeft="true"
    12         android:layout_centerVertical="true"
    13         android:src="@drawable/ic_launcher" />
    14 
    15     <TextView
    16         android:id="@+id/textView"
    17         android:layout_width="wrap_content"
    18         android:layout_height="wrap_content"
    19         android:layout_alignBottom="@+id/imageView1"
    20         android:layout_alignParentRight="true"
    21         android:gravity="center"
    22         android:textSize="20sp"
    23         android:layout_alignTop="@+id/imageView1"
    24         android:layout_toRightOf="@+id/imageView1"
    25         android:text="TextView" />
    26 
    27 </RelativeLayout>
    item.xml

    MainActivity.java:

      1 package com.zzw.testpinnedsectionlistview;
      2 
      3 import java.util.ArrayList;
      4 
      5 import com.zzw.testpinnedsectionlistview.PinnedSectionListView.PinnedSectionListAdapter;
      6 
      7 import android.app.Activity;
      8 import android.content.Context;
      9 import android.graphics.Color;
     10 import android.os.Bundle;
     11 import android.view.LayoutInflater;
     12 import android.view.View;
     13 import android.view.ViewGroup;
     14 import android.widget.ArrayAdapter;
     15 import android.widget.TextView;
     16 
     17 public class MainActivity extends Activity {
     18 
     19     private ArrayList<Item> items = null;
     20 
     21     private final int VIEW_TYPE_COUNT = 2;
     22 
     23     @Override
     24     protected void onCreate(Bundle savedInstanceState) {
     25         super.onCreate(savedInstanceState);
     26         setContentView(R.layout.activity_main);
     27 
     28         items =new ArrayList<MainActivity.Item>();
     29         // 假设我们演示以A,B,C,,,F这样的字符串作为分组的标签。
     30         // 每一组装载5个子数据。
     31         String[] groups = { "A", "B", "C", "D", "E" };
     32         for (int i = 0; i < groups.length; i++) {
     33             String s = groups[i];
     34 
     35             Item group = new Item();
     36             group.type = Item.GROUP;
     37             group.text = s;
     38             items.add(group);
     39 
     40             for (int j = 0; j < 10; j++) {
     41                 Item child = new Item();
     42                 child.type = Item.CHILD;
     43                 child.text = s + "组数据:" + j;
     44                 items.add(child);
     45             }
     46         }
     47 
     48         PinnedSectionListView listView = (PinnedSectionListView) findViewById(R.id.listView);
     49         listView.setAdapter(new MyAdapter(this, -1));
     50 
     51     }
     52 
     53     private class Item {
     54         public static final int GROUP = 0;
     55         public static final int CHILD = 1;
     56 
     57         public int type;
     58         public String text;
     59     }
     60 
     61     private class MyAdapter extends ArrayAdapter<Item> implements PinnedSectionListAdapter {
     62 
     63         private LayoutInflater inflater;
     64 
     65         public MyAdapter(Context context, int resource) {
     66             super(context, resource);
     67 
     68             inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
     69         }
     70 
     71         @Override
     72         public int getItemViewType(int position) {
     73 
     74             return items.get(position).type;
     75         }
     76 
     77         @Override
     78         public int getCount() {
     79 
     80             return items.size();
     81         }
     82 
     83         @Override
     84         public int getViewTypeCount() {
     85 
     86             return VIEW_TYPE_COUNT;
     87         }
     88 
     89         @Override
     90         public Item getItem(int position) {
     91 
     92             return items.get(position);
     93         }
     94 
     95         @Override
     96         public View getView(int position, View convertView, ViewGroup parent) {
     97             switch (getItemViewType(position)) {
     98             case Item.GROUP:
     99 
    100                 if (convertView == null) {
    101                     convertView = inflater.inflate(android.R.layout.simple_list_item_1, null);
    102                 }
    103 
    104                 TextView textView_group = (TextView) convertView.findViewById(android.R.id.text1);
    105                 textView_group.setText(getItem(position).text);
    106                 textView_group.setTextColor(Color.BLUE);
    107                 textView_group.setTextSize(30);
    108                 textView_group.setBackgroundColor(Color.GRAY);
    109 
    110                 break;
    111 
    112             case Item.CHILD:
    113                 if (convertView == null) {
    114                     convertView = inflater.inflate(R.layout.item, null);
    115                 }
    116 
    117                 TextView textView_child = (TextView) convertView.findViewById(R.id.textView);
    118                 textView_child.setText(getItem(position).text);
    119                 textView_child.setBackgroundColor(Color.YELLOW);
    120 
    121                 break;
    122             }
    123             return convertView;
    124         }
    125 
    126         /*
    127          * 假设此方法返回皆为false。那么PinnedSectionListView将退化成为一个基础的ListView.
    128          * 只不过退化后的ListView只是一个拥有两个View Type的ListView。
    129          * 
    130          * 从某种角度上讲,此方法对于PinnedSectionListView至关重要
    131          * 返回值true或false,将直接导致PinnedSectionListView是一个PinnedSectionListView,
    132          * 还是一个普通的ListView
    133          * 
    134          */
    135         @Override
    136         public boolean isItemViewTypePinned(int viewType) {
    137             boolean type = false;
    138             switch (viewType) {
    139             case Item.GROUP:
    140                 type = true;
    141                 break;
    142 
    143             case Item.CHILD:
    144                 type = false;
    145                 break;
    146 
    147             default:
    148                 type = false;
    149                 break;
    150             }
    151             return type;
    152         }
    153     }
    154 }

    程序运行拉动的时候有的sdk版本会出现程序崩溃,LogCat是这样情况:

     

    报错的是原代码PinnedSectionListView.java中的(200行左右):

    // read layout parameters
            LayoutParams layoutParams = (LayoutParams) pinnedView.getLayoutParams();
            if (layoutParams == null) {
                layoutParams = (LayoutParams) generateDefaultLayoutParams();
                pinnedView.setLayoutParams(layoutParams);
            }

    主要是这句话:

    layoutParams = (LayoutParams) generateDefaultLayoutParams();

    经由研究发现,此原因是在调用Android系统的generateDefaultLayoutParams()方法时候,发生异常,致使代码运行获得的结果layoutParams不正常,进而导致PinnedSectionListView崩溃。

    解决方案:
    自己动手重写Android系统的generateDefaultLayoutParams()方法,返回自己定制的LayoutParams值。

    具体实现:
    在PinnedSectionListView.java中增加自己重写的generateDefaultLayoutParams()方法:

    1 @Override  
    2    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {  
    3       
    4     LayoutParams mLayoutParams=new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);  
    5       
    6     return mLayoutParams;  
    7    }  

    最终修复bug,改进后的PinnedSectionListView.java全部源代码为如下(可直接复制粘贴使用):

      1 /*
      2  * Copyright (C) 2013 Sergej Shafarenka, halfbit.de
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file kt in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.zzw.testpinnedsectionlistview;
     18 
     19 
     20 import android.content.Context;
     21 import android.database.DataSetObserver;
     22 import android.graphics.Canvas;
     23 import android.graphics.Color;
     24 import android.graphics.PointF;
     25 import android.graphics.Rect;
     26 import android.graphics.drawable.GradientDrawable;
     27 import android.graphics.drawable.GradientDrawable.Orientation;
     28 import android.os.Parcelable;
     29 import android.util.AttributeSet;
     30 import android.view.MotionEvent;
     31 import android.view.SoundEffectConstants;
     32 import android.view.View;
     33 import android.view.ViewConfiguration;
     34 import android.view.ViewGroup;
     35 import android.view.accessibility.AccessibilityEvent;
     36 import android.widget.AbsListView;
     37 import android.widget.HeaderViewListAdapter;
     38 import android.widget.ListAdapter;
     39 import android.widget.ListView;
     40 import android.widget.SectionIndexer;
     41 
     42 
     43 /**
     44  * ListView, which is capable to pin section views at its top while the rest is still scrolled.
     45  */
     46 public class PinnedSectionListView extends ListView {
     47 
     48     //-- inner classes
     49 
     50     /** List adapter to be implemented for being used with PinnedSectionListView adapter. */
     51     public static interface PinnedSectionListAdapter extends ListAdapter {
     52         /** This method shall return 'true' if views of given type has to be pinned. */
     53         boolean isItemViewTypePinned(int viewType);
     54     }
     55 
     56     /** Wrapper class for pinned section view and its position in the list. */
     57     static class PinnedSection {
     58         public View view;
     59         public int position;
     60         public long id;
     61     }
     62 
     63     //-- class fields
     64 
     65     // fields used for handling touch events
     66     private final Rect mTouchRect = new Rect();
     67     private final PointF mTouchPoint = new PointF();
     68     private int mTouchSlop;
     69     private View mTouchTarget;
     70     private MotionEvent mDownEvent;
     71 
     72     // fields used for drawing shadow under a pinned section
     73     private GradientDrawable mShadowDrawable;
     74     private int mSectionsDistanceY;
     75     private int mShadowHeight;
     76 
     77     /** Delegating listener, can be null. */
     78     OnScrollListener mDelegateOnScrollListener;
     79 
     80     /** Shadow for being recycled, can be null. */
     81     PinnedSection mRecycleSection;
     82 
     83     /** shadow instance with a pinned view, can be null. */
     84     PinnedSection mPinnedSection;
     85 
     86     /** Pinned view Y-translation. We use it to stick pinned view to the next section. */
     87     int mTranslateY;
     88 
     89     /** Scroll listener which does the magic */
     90     private final OnScrollListener mOnScrollListener = new OnScrollListener() {
     91 
     92         @Override public void onScrollStateChanged(AbsListView view, int scrollState) {
     93             if (mDelegateOnScrollListener != null) { // delegate
     94                 mDelegateOnScrollListener.onScrollStateChanged(view, scrollState);
     95             }
     96         }
     97 
     98         @Override
     99         public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
    100 
    101             if (mDelegateOnScrollListener != null) { // delegate
    102                 mDelegateOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
    103             }
    104 
    105             // get expected adapter or fail fast
    106             ListAdapter adapter = getAdapter();
    107             if (adapter == null || visibleItemCount == 0) return; // nothing to do
    108 
    109             final boolean isFirstVisibleItemSection =
    110                     isItemViewTypePinned(adapter, adapter.getItemViewType(firstVisibleItem));
    111 
    112             if (isFirstVisibleItemSection) {
    113                 View sectionView = getChildAt(0);
    114                 if (sectionView.getTop() == getPaddingTop()) { // view sticks to the top, no need for pinned shadow
    115                     destroyPinnedShadow();
    116                 } else { // section doesn't stick to the top, make sure we have a pinned shadow
    117                     ensureShadowForPosition(firstVisibleItem, firstVisibleItem, visibleItemCount);
    118                 }
    119 
    120             } else { // section is not at the first visible position
    121                 int sectionPosition = findCurrentSectionPosition(firstVisibleItem);
    122                 if (sectionPosition > -1) { // we have section position
    123                     ensureShadowForPosition(sectionPosition, firstVisibleItem, visibleItemCount);
    124                 } else { // there is no section for the first visible item, destroy shadow
    125                     destroyPinnedShadow();
    126                 }
    127             }
    128         };
    129 
    130     };
    131 
    132     /** Default change observer. */
    133     private final DataSetObserver mDataSetObserver = new DataSetObserver() {
    134         @Override public void onChanged() {
    135             recreatePinnedShadow();
    136         };
    137         @Override public void onInvalidated() {
    138             recreatePinnedShadow();
    139         }
    140     };
    141 
    142     //-- constructors
    143 
    144     public PinnedSectionListView(Context context, AttributeSet attrs) {
    145         super(context, attrs);
    146         initView();
    147     }
    148 
    149     public PinnedSectionListView(Context context, AttributeSet attrs, int defStyle) {
    150         super(context, attrs, defStyle);
    151         initView();
    152     }
    153 
    154     private void initView() {
    155         setOnScrollListener(mOnScrollListener);
    156         mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
    157         initShadow(true);
    158     }
    159 
    160     //-- public API methods
    161 
    162     public void setShadowVisible(boolean visible) {
    163         initShadow(visible);
    164         if (mPinnedSection != null) {
    165             View v = mPinnedSection.view;
    166             invalidate(v.getLeft(), v.getTop(), v.getRight(), v.getBottom() + mShadowHeight);
    167         }
    168     }
    169 
    170     //-- pinned section drawing methods
    171 
    172     public void initShadow(boolean visible) {
    173         if (visible) {
    174             if (mShadowDrawable == null) {
    175                 mShadowDrawable = new GradientDrawable(Orientation.TOP_BOTTOM,
    176                         new int[] { Color.parseColor("#ffa0a0a0"), Color.parseColor("#50a0a0a0"), Color.parseColor("#00a0a0a0")});
    177                 mShadowHeight = (int) (8 * getResources().getDisplayMetrics().density);
    178             }
    179         } else {
    180             if (mShadowDrawable != null) {
    181                 mShadowDrawable = null;
    182                 mShadowHeight = 0;
    183             }
    184         }
    185     }
    186 
    187     //*****添加*****//
    188     @Override
    189     protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
    190         LayoutParams mLayoutParams=new LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.WRAP_CONTENT);    
    191         return mLayoutParams;
    192     }
    193     //*****添加*****//
    194     
    195     /** Create shadow wrapper with a pinned view for a view at given position */
    196     void createPinnedShadow(int position) {
    197 
    198         // try to recycle shadow
    199         PinnedSection pinnedShadow = mRecycleSection;
    200         mRecycleSection = null;
    201 
    202         // create new shadow, if needed
    203         if (pinnedShadow == null) pinnedShadow = new PinnedSection();
    204         // request new view using recycled view, if such
    205         View pinnedView = getAdapter().getView(position, pinnedShadow.view, PinnedSectionListView.this);
    206 
    207         // read layout parameters
    208         LayoutParams layoutParams = (LayoutParams) pinnedView.getLayoutParams();
    209         if (layoutParams == null) {
    210             layoutParams = (LayoutParams) generateDefaultLayoutParams();
    211             pinnedView.setLayoutParams(layoutParams);
    212         }
    213 
    214         int heightMode = MeasureSpec.getMode(layoutParams.height);
    215         int heightSize = MeasureSpec.getSize(layoutParams.height);
    216 
    217         if (heightMode == MeasureSpec.UNSPECIFIED) heightMode = MeasureSpec.EXACTLY;
    218 
    219         int maxHeight = getHeight() - getListPaddingTop() - getListPaddingBottom();
    220         if (heightSize > maxHeight) heightSize = maxHeight;
    221 
    222         // measure & layout
    223         int ws = MeasureSpec.makeMeasureSpec(getWidth() - getListPaddingLeft() - getListPaddingRight(), MeasureSpec.EXACTLY);
    224         int hs = MeasureSpec.makeMeasureSpec(heightSize, heightMode);
    225         pinnedView.measure(ws, hs);
    226         pinnedView.layout(0, 0, pinnedView.getMeasuredWidth(), pinnedView.getMeasuredHeight());
    227         mTranslateY = 0;
    228 
    229         // initialize pinned shadow
    230         pinnedShadow.view = pinnedView;
    231         pinnedShadow.position = position;
    232         pinnedShadow.id = getAdapter().getItemId(position);
    233 
    234         // store pinned shadow
    235         mPinnedSection = pinnedShadow;
    236     }
    237 
    238     /** Destroy shadow wrapper for currently pinned view */
    239     void destroyPinnedShadow() {
    240         if (mPinnedSection != null) {
    241             // keep shadow for being recycled later
    242             mRecycleSection = mPinnedSection;
    243             mPinnedSection = null;
    244         }
    245     }
    246 
    247     /** Makes sure we have an actual pinned shadow for given position. */
    248     void ensureShadowForPosition(int sectionPosition, int firstVisibleItem, int visibleItemCount) {
    249         if (visibleItemCount < 2) { // no need for creating shadow at all, we have a single visible item
    250             destroyPinnedShadow();
    251             return;
    252         }
    253 
    254         if (mPinnedSection != null
    255                 && mPinnedSection.position != sectionPosition) { // invalidate shadow, if required
    256             destroyPinnedShadow();
    257         }
    258 
    259         if (mPinnedSection == null) { // create shadow, if empty
    260             createPinnedShadow(sectionPosition);
    261         }
    262 
    263         // align shadow according to next section position, if needed
    264         int nextPosition = sectionPosition + 1;
    265         if (nextPosition < getCount()) {
    266             int nextSectionPosition = findFirstVisibleSectionPosition(nextPosition,
    267                     visibleItemCount - (nextPosition - firstVisibleItem));
    268             if (nextSectionPosition > -1) {
    269                 View nextSectionView = getChildAt(nextSectionPosition - firstVisibleItem);
    270                 final int bottom = mPinnedSection.view.getBottom() + getPaddingTop();
    271                 mSectionsDistanceY = nextSectionView.getTop() - bottom;
    272                 if (mSectionsDistanceY < 0) {
    273                     // next section overlaps pinned shadow, move it up
    274                     mTranslateY = mSectionsDistanceY;
    275                 } else {
    276                     // next section does not overlap with pinned, stick to top
    277                     mTranslateY = 0;
    278                 }
    279             } else {
    280                 // no other sections are visible, stick to top
    281                 mTranslateY = 0;
    282                 mSectionsDistanceY = Integer.MAX_VALUE;
    283             }
    284         }
    285 
    286     }
    287 
    288     int findFirstVisibleSectionPosition(int firstVisibleItem, int visibleItemCount) {
    289         ListAdapter adapter = getAdapter();
    290 
    291         int adapterDataCount = adapter.getCount();
    292         if (getLastVisiblePosition() >= adapterDataCount) return -1; // dataset has changed, no candidate
    293 
    294         if (firstVisibleItem+visibleItemCount >= adapterDataCount){//added to prevent index Outofbound (in case)
    295             visibleItemCount = adapterDataCount-firstVisibleItem;
    296         }
    297 
    298         for (int childIndex = 0; childIndex < visibleItemCount; childIndex++) {
    299             int position = firstVisibleItem + childIndex;
    300             int viewType = adapter.getItemViewType(position);
    301             if (isItemViewTypePinned(adapter, viewType)) return position;
    302         }
    303         return -1;
    304     }
    305 
    306     int findCurrentSectionPosition(int fromPosition) {
    307         ListAdapter adapter = getAdapter();
    308 
    309         if (fromPosition >= adapter.getCount()) return -1; // dataset has changed, no candidate
    310         
    311         if (adapter instanceof SectionIndexer) {
    312             // try fast way by asking section indexer
    313             SectionIndexer indexer = (SectionIndexer) adapter;
    314             int sectionPosition = indexer.getSectionForPosition(fromPosition);
    315             int itemPosition = indexer.getPositionForSection(sectionPosition);
    316             int typeView = adapter.getItemViewType(itemPosition);
    317             if (isItemViewTypePinned(adapter, typeView)) {
    318                 return itemPosition;
    319             } // else, no luck
    320         }
    321 
    322         // try slow way by looking through to the next section item above
    323         for (int position=fromPosition; position>=0; position--) {
    324             int viewType = adapter.getItemViewType(position);
    325             if (isItemViewTypePinned(adapter, viewType)) return position;
    326         }
    327         return -1; // no candidate found
    328     }
    329 
    330     void recreatePinnedShadow() {
    331         destroyPinnedShadow();
    332         ListAdapter adapter = getAdapter();
    333         if (adapter != null && adapter.getCount() > 0) {
    334             int firstVisiblePosition = getFirstVisiblePosition();
    335             int sectionPosition = findCurrentSectionPosition(firstVisiblePosition);
    336             if (sectionPosition == -1) return; // no views to pin, exit
    337             ensureShadowForPosition(sectionPosition,
    338                     firstVisiblePosition, getLastVisiblePosition() - firstVisiblePosition);
    339         }
    340     }
    341 
    342     @Override
    343     public void setOnScrollListener(OnScrollListener listener) {
    344         if (listener == mOnScrollListener) {
    345             super.setOnScrollListener(listener);
    346         } else {
    347             mDelegateOnScrollListener = listener;
    348         }
    349     }
    350 
    351     @Override
    352     public void onRestoreInstanceState(Parcelable state) {
    353         super.onRestoreInstanceState(state);
    354         post(new Runnable() {
    355             @Override public void run() { // restore pinned view after configuration change
    356                 recreatePinnedShadow();
    357             }
    358         });
    359     }
    360 
    361     @Override
    362     public void setAdapter(ListAdapter adapter) {
    363 
    364         // assert adapter in debug mode
    365         if (BuildConfig.DEBUG && adapter != null) {
    366             if (!(adapter instanceof PinnedSectionListAdapter))
    367                 throw new IllegalArgumentException("Does your adapter implement PinnedSectionListAdapter?");
    368             if (adapter.getViewTypeCount() < 2)
    369                 throw new IllegalArgumentException("Does your adapter handle at least two types" +
    370                         " of views in getViewTypeCount() method: items and sections?");
    371         }
    372 
    373         // unregister observer at old adapter and register on new one
    374         ListAdapter oldAdapter = getAdapter();
    375         if (oldAdapter != null) oldAdapter.unregisterDataSetObserver(mDataSetObserver);
    376         if (adapter != null) adapter.registerDataSetObserver(mDataSetObserver);
    377 
    378         // destroy pinned shadow, if new adapter is not same as old one
    379         if (oldAdapter != adapter) destroyPinnedShadow();
    380 
    381         super.setAdapter(adapter);
    382     }
    383 
    384     @Override
    385     protected void onLayout(boolean changed, int l, int t, int r, int b) {
    386         super.onLayout(changed, l, t, r, b);
    387         if (mPinnedSection != null) {
    388             int parentWidth = r - l - getPaddingLeft() - getPaddingRight();
    389             int shadowWidth = mPinnedSection.view.getWidth();
    390             if (parentWidth != shadowWidth) {
    391                 recreatePinnedShadow();
    392             }
    393         }
    394     }
    395 
    396     @Override
    397     protected void dispatchDraw(Canvas canvas) {
    398         super.dispatchDraw(canvas);
    399 
    400         if (mPinnedSection != null) {
    401 
    402             // prepare variables
    403             int pLeft = getListPaddingLeft();
    404             int pTop = getListPaddingTop();
    405             View view = mPinnedSection.view;
    406 
    407             // draw child
    408             canvas.save();
    409 
    410             int clipHeight = view.getHeight() +
    411                     (mShadowDrawable == null ? 0 : Math.min(mShadowHeight, mSectionsDistanceY));
    412             canvas.clipRect(pLeft, pTop, pLeft + view.getWidth(), pTop + clipHeight);
    413 
    414             canvas.translate(pLeft, pTop + mTranslateY);
    415             drawChild(canvas, mPinnedSection.view, getDrawingTime());
    416 
    417             if (mShadowDrawable != null && mSectionsDistanceY > 0) {
    418                 mShadowDrawable.setBounds(mPinnedSection.view.getLeft(),
    419                         mPinnedSection.view.getBottom(),
    420                         mPinnedSection.view.getRight(),
    421                         mPinnedSection.view.getBottom() + mShadowHeight);
    422                 mShadowDrawable.draw(canvas);
    423             }
    424 
    425             canvas.restore();
    426         }
    427     }
    428 
    429     //-- touch handling methods
    430 
    431     @Override
    432     public boolean dispatchTouchEvent(MotionEvent ev) {
    433 
    434         final float x = ev.getX();
    435         final float y = ev.getY();
    436         final int action = ev.getAction();
    437 
    438         if (action == MotionEvent.ACTION_DOWN
    439                 && mTouchTarget == null
    440                 && mPinnedSection != null
    441                 && isPinnedViewTouched(mPinnedSection.view, x, y)) { // create touch target
    442 
    443             // user touched pinned view
    444             mTouchTarget = mPinnedSection.view;
    445             mTouchPoint.x = x;
    446             mTouchPoint.y = y;
    447 
    448             // copy down event for eventually be used later
    449             mDownEvent = MotionEvent.obtain(ev);
    450         }
    451 
    452         if (mTouchTarget != null) {
    453             if (isPinnedViewTouched(mTouchTarget, x, y)) { // forward event to pinned view
    454                 mTouchTarget.dispatchTouchEvent(ev);
    455             }
    456 
    457             if (action == MotionEvent.ACTION_UP) { // perform onClick on pinned view
    458                 super.dispatchTouchEvent(ev);
    459                 performPinnedItemClick();
    460                 clearTouchTarget();
    461 
    462             } else if (action == MotionEvent.ACTION_CANCEL) { // cancel
    463                 clearTouchTarget();
    464 
    465             } else if (action == MotionEvent.ACTION_MOVE) {
    466                 if (Math.abs(y - mTouchPoint.y) > mTouchSlop) {
    467 
    468                     // cancel sequence on touch target
    469                     MotionEvent event = MotionEvent.obtain(ev);
    470                     event.setAction(MotionEvent.ACTION_CANCEL);
    471                     mTouchTarget.dispatchTouchEvent(event);
    472                     event.recycle();
    473 
    474                     // provide correct sequence to super class for further handling
    475                     super.dispatchTouchEvent(mDownEvent);
    476                     super.dispatchTouchEvent(ev);
    477                     clearTouchTarget();
    478 
    479                 }
    480             }
    481 
    482             return true;
    483         }
    484 
    485         // call super if this was not our pinned view
    486         return super.dispatchTouchEvent(ev);
    487     }
    488 
    489     private boolean isPinnedViewTouched(View view, float x, float y) {
    490         view.getHitRect(mTouchRect);
    491 
    492         // by taping top or bottom padding, the list performs on click on a border item.
    493         // we don't add top padding here to keep behavior consistent.
    494         mTouchRect.top += mTranslateY;
    495 
    496         mTouchRect.bottom += mTranslateY + getPaddingTop();
    497         mTouchRect.left += getPaddingLeft();
    498         mTouchRect.right -= getPaddingRight();
    499         return mTouchRect.contains((int)x, (int)y);
    500     }
    501 
    502     private void clearTouchTarget() {
    503         mTouchTarget = null;
    504         if (mDownEvent != null) {
    505             mDownEvent.recycle();
    506             mDownEvent = null;
    507         }
    508     }
    509 
    510     private boolean performPinnedItemClick() {
    511         if (mPinnedSection == null) return false;
    512 
    513         OnItemClickListener listener = getOnItemClickListener();
    514         if (listener != null && getAdapter().isEnabled(mPinnedSection.position)) {
    515             View view =  mPinnedSection.view;
    516             playSoundEffect(SoundEffectConstants.CLICK);
    517             if (view != null) {
    518                 view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
    519             }
    520             listener.onItemClick(this, view, mPinnedSection.position, mPinnedSection.id);
    521             return true;
    522         }
    523         return false;
    524     }
    525 
    526     public static boolean isItemViewTypePinned(ListAdapter adapter, int viewType) {
    527         if (adapter instanceof HeaderViewListAdapter) {
    528             adapter = ((HeaderViewListAdapter)adapter).getWrappedAdapter();
    529         }
    530         return ((PinnedSectionListAdapter) adapter).isItemViewTypePinned(viewType);
    531     }
    532 
    533 }
    PinnedSectionListView.java
  • 相关阅读:
    高斯模糊原理,算法
    SIFT算法详解
    第五章:状态图
    ANTLR4权威指南
    第八章:包图,组件图,部署图
    棋盘n皇后问题-递归
    普通页面引入React(使用和不使用JSX)
    浏览器环境
    DevTool-Network
    优化浏览器渲染
  • 原文地址:https://www.cnblogs.com/zzw1994/p/4997601.html
Copyright © 2011-2022 走看看