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
  • 相关阅读:
    微人事项目-mybatis-持久层
    通过外键连接多个表
    springioc
    Redis 消息中间件 ServiceStack.Redis 轻量级
    深度数据对接 链接服务器 数据传输
    sqlserver 抓取所有执行语句 SQL语句分析 死锁 抓取
    sqlserver 索引优化 CPU占用过高 执行分析 服务器检查
    sql server 远程备份 bak 删除
    冒泡排序
    多线程 异步 beginInvoke EndInvoke 使用
  • 原文地址:https://www.cnblogs.com/zzw1994/p/4997601.html
Copyright © 2011-2022 走看看