zoukankan      html  css  js  c++  java
  • Android 自定义列表指示器

    在联系人界面 可以看到这种界面 手指快速滑动右边滑动条时 可以显示相应的字母图标

    android里提供了android.widget.SectionIndexer这个接口去实现该效果 可是只能显示字母提示

    但我们想实现右图这种效果时 则只能自定义去实现了

    先上代码

    MainActivity

    public class MainActivity extends Activity {
    
      /*
       * 
       * 1 ListView BaseAdapter DataSource 列表 适配器 数据源
       * 巨坑 外面只有包上线性布局才会显示
       * 
       */
    
      @Override
      protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    
        ListView listView = (ListView) findViewById(R.id.list);
    
        List<Model> models = new ArrayList<Model>();
    
        for (int i = 0; i < 400; i++) {
    
          Model model = new Model("key" + i, "2012" + i);
          models.add(model);
        }
        
        MyAdapter1 adapter = new MyAdapter1(getApplicationContext(), models);
        
        listView.setAdapter(adapter);
        
      }
    
    }

    activity_main

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:demoapp="http://schemas.android.com/apk/res/com.example.mylistview"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="vertical" >
        
            <com.example.mylistview.CustomFastScrollView
            android:id="@+id/fast_scroll_view"
            android:layout_width="wrap_content"
            android:layout_height="0px"
            android:background="#FFFFFF"
            android:layout_weight="1"
            demoapp:overlayHeight="40dp"
            demoapp:overlayScrollThumbWidth="40dp"
            demoapp:overlayTextSize="18dp"
            demoapp:overlayWidth="100dp" >
    
            <ListView
                android:id="@+id/list"
                android:layout_width="wrap_content"
                android:layout_height="fill_parent" />
    
        </com.example.mylistview.CustomFastScrollView>
        
    
    </LinearLayout>

    Model

    public class Model {
    
      private String key; // 列表中显示
      private String value; // 提示中显示
      private String other;
    
      public Model(String key, String value) {
        super();
        this.key = key;
        this.value = value;
      }
    
      /**
       * @return the key
       */
      public String getKey() {
        return this.key;
      }
    
      /**
       * @param key
       *          the key to set
       */
      public void setKey(String key) {
        this.key = key;
      }
    
      /**
       * @return the value
       */
      public String getValue() {
        return this.value;
      }
    
      /**
       * @param value
       *          the value to set
       */
      public void setValue(String value) {
        this.value = value;
      }
    
    }

    MyAdapter1

    public class MyAdapter1 extends BaseAdapter implements SectionIndexer{
      private static final String TAG = "MyAdapter";
    
      private List<Model> countries;
      private Context context;
      private SectionIndexer sectionIndexer;
    
      public MyAdapter1(Context context, List<Model> countries) {
    
        this.context = context;
        this.countries = countries;
    
      }
    
      @Override
      public int getCount() {
        return countries.size();
      }
    
      @Override
      public Object getItem(int position) {
        return null;
      }
    
      @Override
      public long getItemId(int position) {
        return position;
      }
    
      @Override
      public View getView(int position, View convertView, ViewGroup parent) {
    
        // 创建一个LinearLayout,并向其中添加2个组件
        LinearLayout line = new LinearLayout(context);
        line.setOrientation(0);
        ImageView image = new ImageView(context);
        image.setImageResource(R.drawable.ic_launcher);
        TextView text = new TextView(context);
        text.setText(countries.get(position).getKey());
        text.setTextSize(20);
        text.setTextColor(Color.RED);
        line.addView(image);
        line.addView(text);
        // 返回LinearLayout实例
        return line;
    
      }
    
      @Override
      public Object[] getSections() {
        return getSectionIndexer().getSections();
      }
    
      @Override
      public int getPositionForSection(int section) {
        return getSectionIndexer().getPositionForSection(section);
      }
    
      @Override
      public int getSectionForPosition(int position) {
        return getSectionIndexer().getSectionForPosition(position);
      }
    
      private SectionIndexer getSectionIndexer() {
        if (sectionIndexer == null) {
          sectionIndexer = createSectionIndexer(countries);
        }
        return sectionIndexer;
      }
    
      private SectionIndexer createSectionIndexer(List<Model> countries) {
    
        return createSectionIndexer(countries, new Function<Model, String>() {
          @Override
          public String apply(Model input) {
            // show just the first letter in the title
            //return input.getName().substring(0, 1);
            
            return input.getValue();
          }
        });
      }
      
      private SectionIndexer createSectionIndexer(List<Model> countries, Function<Model, String> sectionFunction) {
    
        List<String> sections = new ArrayList<String>();
        final List<Integer> sectionsToPositions = new ArrayList<Integer>();
        final List<Integer> positionsToSections = new ArrayList<Integer>();
    
        for (int i = 0; i < countries.size(); i++) {
          Model country = countries.get(i);
          String section = sectionFunction.apply(country);
          
          Log.i("TAG1", "section----" + section);
          
          if (sections.isEmpty() || !sections.get(sections.size() - 1).equals(section)) {
            sections.add(section);
            sectionsToPositions.add(i);
          }
    
          positionsToSections.add(sections.size() - 1);
        }
    
        final String[] sectionsArray = sections.toArray(new String[sections.size()]);
    
        return new SectionIndexer() {
    
          @Override
          public Object[] getSections() {
            return sectionsArray;
          }
    
          @Override
          public int getSectionForPosition(int position) {
            return positionsToSections.get(position);
          }
    
          @Override
          public int getPositionForSection(int section) {
            return sectionsToPositions.get(section);
          }
        };
      }
      
      
      
    }

    Function

    public interface Function<E,T> {
        
        public T apply(E input);
    
    }

    最后最关键的类

    CustomFastScrollView

    public class CustomFastScrollView extends FrameLayout 
            implements OnScrollListener, OnHierarchyChangeListener {
    
        // how much transparency to use for the fast scroll thumb
        private static final int ALPHA_MAX = 255;
        
        // how long before the fast scroll thumb disappears
        private static final long FADE_DURATION = 200;
        
        private Drawable mCurrentThumb; //滑动条
        private Drawable mOverlayDrawable; //提示文字背景框
    
        private int mThumbH;
        private int mThumbW;
        private int mThumbY;
    
        private RectF mOverlayPos;
    
        // custom values I defined
        private int mOverlayWidth;
        private int mOverlayHeight;
        private float mOverlayTextSize;
        private int mOverlayScrollThumbWidth;
    
        private boolean mDragging;
        private ListView mList;
        private boolean mScrollCompleted;
        private boolean mThumbVisible;
        private int mVisibleItem;
        private Paint mPaint;
        private int mListOffset;
    
        private Object [] mSections;
        private String mSectionText;
        private boolean mDrawOverlay;
        private ScrollFade mScrollFade;
    
        private Handler mHandler = new Handler();
    
        private BaseAdapter mListAdapter;
    
        private boolean mChangedBounds;
    
        public static interface SectionIndexer {
            Object[] getSections();
    
            int getPositionForSection(int section);
    
            int getSectionForPosition(int position);
        }
    
        public CustomFastScrollView(Context context) {
            super(context);
    
            init(context, null);
        }
    
    
        public CustomFastScrollView(Context context, AttributeSet attrs) {
            super(context, attrs);
    
            init(context, attrs);
        }
    
        public CustomFastScrollView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
    
            init(context, attrs);
        }
    
        private void useThumbDrawable(Drawable drawable) {
            mCurrentThumb = drawable;
            mThumbW = mOverlayScrollThumbWidth;//mCurrentThumb.getIntrinsicWidth();
            mThumbH = mCurrentThumb.getIntrinsicHeight();
            mChangedBounds = true;
        }
    
        private void init(Context context, AttributeSet attrs) {
    
            // set all attributes from xml
            if (attrs != null) {
                TypedArray typedArray = context.obtainStyledAttributes(attrs,
                        R.styleable.CustomFastScrollView);
                mOverlayHeight = typedArray.getDimensionPixelSize(
                        R.styleable.CustomFastScrollView_overlayHeight, 0);
                mOverlayWidth = typedArray.getDimensionPixelSize(
                        R.styleable.CustomFastScrollView_overlayWidth, 0);
                mOverlayTextSize = typedArray.getDimensionPixelSize(
                        R.styleable.CustomFastScrollView_overlayTextSize, 0);
                mOverlayScrollThumbWidth = typedArray.getDimensionPixelSize(
                        R.styleable.CustomFastScrollView_overlayScrollThumbWidth, 0);
    
            }
    
            // Get both the scrollbar states drawables
            final Resources res = context.getResources();
            //Drawable thumbDrawable = res.getDrawable(R.drawable.scrollbar_handle_accelerated_anim2);
            Drawable thumbDrawable = res.getDrawable(R.drawable.bar1);
            useThumbDrawable(thumbDrawable);
    
            //mOverlayDrawable = res.getDrawable(android.R.drawable.alert_dark_frame);
            mOverlayDrawable = res.getDrawable(R.drawable.bar2);
    
            mScrollCompleted = true;
            setWillNotDraw(false);
    
            // Need to know when the ListView is added
            setOnHierarchyChangeListener(this);
    
            mOverlayPos = new RectF();
            mScrollFade = new ScrollFade();
            mPaint = new Paint();
            mPaint.setAntiAlias(true);
            mPaint.setTextAlign(Paint.Align.CENTER);
            mPaint.setTextSize(mOverlayTextSize);
            mPaint.setColor(0xFFFFFFFF);
            mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        }
    
        private void removeThumb() {
            mThumbVisible = false;
            // Draw one last time to remove thumb
            invalidate();
        }
    
        @Override
        public void draw(Canvas canvas) {
            super.draw(canvas);
    
            if (!mThumbVisible) {
                // No need to draw the rest
                return;
            }
    
            final int y = mThumbY;
            final int viewWidth = getWidth();
            final CustomFastScrollView.ScrollFade scrollFade = mScrollFade;
    
            int alpha = -1;
            if (scrollFade.mStarted) {
                alpha = scrollFade.getAlpha();
                if (alpha < ALPHA_MAX / 2) {
                    mCurrentThumb.setAlpha(alpha * 2);
                }
                int left = viewWidth - (mThumbW * alpha) / ALPHA_MAX;
                mCurrentThumb.setBounds(left, 0, viewWidth, mThumbH);
                
                mChangedBounds = true;
            }
    
            canvas.translate(0, y);
            Log.i("TAG", "left y上的变化 draw" + y);
            mCurrentThumb.draw(canvas);
            
            //canvas.translate(0, -y);
            Log.i("TAG", "left y上的变化 draw" + y);
    
            // If user is dragging the scroll bar, draw the alphabet overlay
            if (mDragging && mDrawOverlay) {
                mOverlayDrawable.draw(canvas);
                final Paint paint = mPaint;
                float descent = paint.descent();
                final RectF rectF = mOverlayPos;
                canvas.drawText(mSectionText + "美丽" , (int) (rectF.left + rectF.right) / 2, (int) (rectF.bottom + rectF.top) / 2 + descent, paint);
            } else if (alpha == 0) {
                scrollFade.mStarted = false;
                removeThumb();
            } else {
                invalidate(viewWidth - mThumbW, y, viewWidth, y + mThumbH);            
            }
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            if (mCurrentThumb != null) {
                mCurrentThumb.setBounds(w - mThumbW, 0, w, mThumbH);
            }
            final RectF pos = mOverlayPos;
            //pos.left = (w - mOverlayWidth) / 2;
            pos.left = w-mOverlayWidth-mThumbW;
            pos.right = pos.left + mOverlayWidth;
            //pos.top = h / 10; // 10% from top
            pos.top = 0 - mOverlayHeight/2 + mThumbH/2;
            pos.bottom = pos.top + mOverlayHeight;
            mOverlayDrawable.setBounds((int) pos.left, (int) pos.top,
                    (int) pos.right, (int) pos.bottom);
        }
    
        public void onScrollStateChanged(AbsListView view, int scrollState) {
        }
    
        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, 
                int totalItemCount) {
    
    
            if (totalItemCount - visibleItemCount > 0 && !mDragging) {
                mThumbY = ((getHeight() - mThumbH) * firstVisibleItem) / (totalItemCount - visibleItemCount);
                if (mChangedBounds) {
                    final int viewWidth = getWidth();
                    mCurrentThumb.setBounds(viewWidth - mThumbW, 0, viewWidth, mThumbH);
                    mChangedBounds = false;
                }
            }
            mScrollCompleted = true;
            if (firstVisibleItem == mVisibleItem) {
                return;
            }
            mVisibleItem = firstVisibleItem;
            if (!mThumbVisible || mScrollFade.mStarted) {
                mThumbVisible = true;
                mCurrentThumb.setAlpha(ALPHA_MAX);
            }
            mHandler.removeCallbacks(mScrollFade);
            mScrollFade.mStarted = false;
            if (!mDragging) {
                mHandler.postDelayed(mScrollFade, 1500);
            }
        }
    
    
        private void getSections() {
            Adapter adapter = mList.getAdapter();
            if (adapter instanceof HeaderViewListAdapter) {
                mListOffset = ((HeaderViewListAdapter)adapter).getHeadersCount();
                adapter = ((HeaderViewListAdapter)adapter).getWrappedAdapter();
            }
            if (adapter instanceof SectionIndexer) {
                mListAdapter = (BaseAdapter) adapter;
                mSections = ((SectionIndexer) mListAdapter).getSections();
            }
        }
    
        public void onChildViewAdded(View parent, View child) {
            if (child instanceof ListView) {
                mList = (ListView)child;
    
                mList.setOnScrollListener(this);
                getSections();
            }
        }
    
        public void onChildViewRemoved(View parent, View child) {
            if (child == mList) {
                mList = null;
                mListAdapter = null;
                mSections = null;
            }
        }
    
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            if (mThumbVisible && ev.getAction() == MotionEvent.ACTION_DOWN) {
                if (ev.getX() > getWidth() - mThumbW && ev.getY() >= mThumbY &&
                        ev.getY() <= mThumbY + mThumbH) {
                    mDragging = true;
                    return true;
                }            
            }
            return false;
        }
    
        private void scrollTo(float position) {
            int count = mList.getCount();
            mScrollCompleted = false;
            final Object[] sections = mSections;
            int sectionIndex;
            if (sections != null && sections.length > 1) {
                final int nSections = sections.length;
    
                int section = (int) (position * nSections);
                if (section >= nSections) {
                    section = nSections - 1;
                }
                sectionIndex = section;
                final SectionIndexer baseAdapter = (SectionIndexer) mListAdapter;
                int index = baseAdapter.getPositionForSection(section);
    
                // Given the expected section and index, the following code will
                // try to account for missing sections (no names starting with..)
                // It will compute the scroll space of surrounding empty sections
                // and interpolate the currently visible letter's range across the
                // available space, so that there is always some list movement while
                // the user moves the thumb.
                int nextIndex = count;
                int prevIndex = index;
                int prevSection = section;
                int nextSection = section + 1;
                // Assume the next section is unique
                if (section < nSections - 1) {
                    nextIndex = baseAdapter.getPositionForSection(section + 1);
                }
    
                // Find the previous index if we're slicing the previous section
                if (nextIndex == index) {
                    // Non-existent letter
                    while (section > 0) {
                        section--;
                         prevIndex = baseAdapter.getPositionForSection(section);
                         if (prevIndex != index) {
                             prevSection = section;
                             sectionIndex = section;
                             break;
                         }
                    }
                }
                // Find the next index, in case the assumed next index is not
                // unique. For instance, if there is no P, then request for P's 
                // position actually returns Q's. So we need to look ahead to make
                // sure that there is really a Q at Q's position. If not, move 
                // further down...
                int nextNextSection = nextSection + 1;
                while (nextNextSection < nSections &&
                        baseAdapter.getPositionForSection(nextNextSection) == nextIndex) {
                    nextNextSection++;
                    nextSection++;
                }
                // Compute the beginning and ending scroll range percentage of the
                // currently visible letter. This could be equal to or greater than
                // (1 / nSections). 
                float fPrev = (float) prevSection / nSections;
                float fNext = (float) nextSection / nSections;
                index = prevIndex + (int) ((nextIndex - prevIndex) * (position - fPrev) 
                        / (fNext - fPrev));
                // Don't overflow
                if (index > count - 1) index = count - 1;
    
                mList.setSelectionFromTop(index + mListOffset, 0);
            } else {
                int index = (int) (position * count);
                mList.setSelectionFromTop(index + mListOffset, 0);
                sectionIndex = -1;
            }
    
            if (sectionIndex >= 0) {
                String text = mSectionText = sections[sectionIndex].toString();
                mDrawOverlay = (text.length() != 1 || text.charAt(0) != ' ') &&
                        sectionIndex < sections.length;
            } else {
                mDrawOverlay = false;
            }
        }
    
        private void cancelFling() {
            // Cancel the list fling
            MotionEvent cancelFling = MotionEvent.obtain(0, 0, MotionEvent.ACTION_CANCEL, 0, 0, 0);
            mList.onTouchEvent(cancelFling);
            cancelFling.recycle();
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent me) {
            if (me.getAction() == MotionEvent.ACTION_DOWN) {
                if (me.getX() > getWidth() - mThumbW
                        && me.getY() >= mThumbY 
                        && me.getY() <= mThumbY + mThumbH) {
    
                    mDragging = true;
                    if (mListAdapter == null && mList != null) {
                        getSections();
                    }
    
                    cancelFling();
                    return true;
                }
            } else if (me.getAction() == MotionEvent.ACTION_UP) {
                if (mDragging) {
                    mDragging = false;
                    final Handler handler = mHandler;
                    handler.removeCallbacks(mScrollFade);
                    handler.postDelayed(mScrollFade, 1000);
                    return true;
                }
            } else if (me.getAction() == MotionEvent.ACTION_MOVE) {
                if (mDragging) {
                    final int viewHeight = getHeight();
                    mThumbY = (int) me.getY() - mThumbH + 10;
                    if (mThumbY < 0) {
                        mThumbY = 0;
                    } else if (mThumbY + mThumbH > viewHeight) {
                        mThumbY = viewHeight - mThumbH;
                    }
                    // If the previous scrollTo is still pending
                    if (mScrollCompleted) {
                        scrollTo((float) mThumbY / (viewHeight - mThumbH));
                    }
                    return true;
                }
            }
    
            return super.onTouchEvent(me);
        }
    
        public class ScrollFade implements Runnable {
    
            long mStartTime;
            long mFadeDuration;
            boolean mStarted;
    
            void startFade() {
                mFadeDuration = FADE_DURATION;
                mStartTime = SystemClock.uptimeMillis();
                mStarted = true;
            }
    
            int getAlpha() {
                if (!mStarted) {
                    return ALPHA_MAX;
                }
                int alpha;
                long now = SystemClock.uptimeMillis();
                if (now > mStartTime + mFadeDuration) {
                    alpha = 0;
                } else {
                    alpha = (int) (ALPHA_MAX - ((now - mStartTime) * ALPHA_MAX) / mFadeDuration); 
                }
                return alpha;
            }
    
            public void run() {
                if (!mStarted) {
                    startFade();
                    invalidate();
                }
    
                if (getAlpha() > 0) {
                    final int y = mThumbY;
                    final int viewWidth = getWidth();
                    invalidate(viewWidth - mThumbW, y, viewWidth, y + mThumbH);
                } else {
                    mStarted = false;
                    removeThumb();
                }
            }
        }
        
        /**
         * Call when the list's items have changed
         */
        public void listItemsChanged() {
            getSections();
        }
    }

     Demo实现的效果如下 下面来分析下代码实现的原理

    Demo见

    https://github.com/huanyi0723/MyListView/

  • 相关阅读:
    java 使用递归获取指定文件路径目录,删除指定的文件后缀(可扩展,根据具体需要的删除的后缀进行配置)~~
    图解Git命令
    Shell脚本编程30分钟入门
    算法系列【希尔排序】篇
    JavaScript、Python、java、Go算法系列之【快速排序】篇
    JavaScript算法 ,Python算法,Go算法,java算法,系列之【归并排序】篇
    JavaScript ,Python,java,C#,Go系列算法之【插入排序篇】
    JavaScript ,Python,java,Go系列算法之【选择排序】篇
    网络编程的演进——从Apache到Nginx
    将博客搬至CSDN
  • 原文地址:https://www.cnblogs.com/huanyi0723/p/4813508.html
Copyright © 2011-2022 走看看