Home | History | Annotate | Download | only in widget
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except 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 package com.android.internal.widget;
     17 
     18 import android.annotation.IdRes;
     19 import android.content.Context;
     20 import android.util.AttributeSet;
     21 import android.view.View;
     22 import android.view.ViewGroup;
     23 import android.widget.AdapterView;
     24 import android.widget.ListAdapter;
     25 import android.widget.ListView;
     26 import android.widget.HeaderViewListAdapter;
     27 
     28 import java.util.ArrayList;
     29 import java.util.function.Predicate;
     30 
     31 public class WatchHeaderListView extends ListView {
     32     private View mTopPanel;
     33 
     34     public WatchHeaderListView(Context context, AttributeSet attrs) {
     35         super(context, attrs);
     36     }
     37 
     38     public WatchHeaderListView(Context context, AttributeSet attrs, int defStyleAttr) {
     39         super(context, attrs, defStyleAttr);
     40     }
     41 
     42     public WatchHeaderListView(
     43             Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
     44         super(context, attrs, defStyleAttr, defStyleRes);
     45     }
     46 
     47     @Override
     48     protected HeaderViewListAdapter wrapHeaderListAdapterInternal(
     49             ArrayList<ListView.FixedViewInfo> headerViewInfos,
     50             ArrayList<ListView.FixedViewInfo> footerViewInfos,
     51             ListAdapter adapter) {
     52         return new WatchHeaderListAdapter(headerViewInfos, footerViewInfos, adapter);
     53     }
     54 
     55     @Override
     56     public void addView(View child, ViewGroup.LayoutParams params) {
     57         if (mTopPanel == null) {
     58             setTopPanel(child);
     59         } else {
     60             throw new IllegalStateException("WatchHeaderListView can host only one header");
     61         }
     62     }
     63 
     64     public void setTopPanel(View v) {
     65         mTopPanel = v;
     66         wrapAdapterIfNecessary();
     67     }
     68 
     69     @Override
     70     public void setAdapter(ListAdapter adapter) {
     71         super.setAdapter(adapter);
     72         wrapAdapterIfNecessary();
     73     }
     74 
     75     @Override
     76     protected View findViewTraversal(@IdRes int id) {
     77         View v = super.findViewTraversal(id);
     78         if (v == null && mTopPanel != null && !mTopPanel.isRootNamespace()) {
     79             return mTopPanel.findViewById(id);
     80         }
     81         return v;
     82     }
     83 
     84     @Override
     85     protected View findViewWithTagTraversal(Object tag) {
     86         View v = super.findViewWithTagTraversal(tag);
     87         if (v == null && mTopPanel != null && !mTopPanel.isRootNamespace()) {
     88             return mTopPanel.findViewWithTag(tag);
     89         }
     90         return v;
     91     }
     92 
     93     @Override
     94     protected <T extends View> T findViewByPredicateTraversal(
     95             Predicate<View> predicate, View childToSkip) {
     96         View v = super.findViewByPredicateTraversal(predicate, childToSkip);
     97         if (v == null && mTopPanel != null && mTopPanel != childToSkip
     98                 && !mTopPanel.isRootNamespace()) {
     99             return (T) mTopPanel.findViewByPredicate(predicate);
    100         }
    101         return (T) v;
    102     }
    103 
    104     @Override
    105     public int getHeaderViewsCount() {
    106         return mTopPanel == null ? super.getHeaderViewsCount()
    107                 : super.getHeaderViewsCount() + (mTopPanel.getVisibility() == GONE ? 0 : 1);
    108     }
    109 
    110     private void wrapAdapterIfNecessary() {
    111         ListAdapter adapter = getAdapter();
    112         if (adapter != null && mTopPanel != null) {
    113             if (!(adapter instanceof WatchHeaderListAdapter)) {
    114                 wrapHeaderListAdapterInternal();
    115             }
    116 
    117             ((WatchHeaderListAdapter) getAdapter()).setTopPanel(mTopPanel);
    118             dispatchDataSetObserverOnChangedInternal();
    119         }
    120     }
    121 
    122     private static class WatchHeaderListAdapter extends HeaderViewListAdapter {
    123         private View mTopPanel;
    124 
    125         public WatchHeaderListAdapter(
    126                 ArrayList<ListView.FixedViewInfo> headerViewInfos,
    127                 ArrayList<ListView.FixedViewInfo> footerViewInfos,
    128                 ListAdapter adapter) {
    129             super(headerViewInfos, footerViewInfos, adapter);
    130         }
    131 
    132         public void setTopPanel(View v) {
    133             mTopPanel = v;
    134         }
    135 
    136         private int getTopPanelCount() {
    137             return (mTopPanel == null || mTopPanel.getVisibility() == GONE) ? 0 : 1;
    138         }
    139 
    140         @Override
    141         public int getCount() {
    142             return super.getCount() + getTopPanelCount();
    143         }
    144 
    145         @Override
    146         public boolean areAllItemsEnabled() {
    147             return getTopPanelCount() == 0 && super.areAllItemsEnabled();
    148         }
    149 
    150         @Override
    151         public boolean isEnabled(int position) {
    152             int topPanelCount = getTopPanelCount();
    153             return position < topPanelCount ? false : super.isEnabled(position - topPanelCount);
    154         }
    155 
    156         @Override
    157         public Object getItem(int position) {
    158             int topPanelCount = getTopPanelCount();
    159             return position < topPanelCount ? null : super.getItem(position - topPanelCount);
    160         }
    161 
    162         @Override
    163         public long getItemId(int position) {
    164             int numHeaders = getHeadersCount() + getTopPanelCount();
    165             if (getWrappedAdapter() != null && position >= numHeaders) {
    166                 int adjPosition = position - numHeaders;
    167                 int adapterCount = getWrappedAdapter().getCount();
    168                 if (adjPosition < adapterCount) {
    169                     return getWrappedAdapter().getItemId(adjPosition);
    170                 }
    171             }
    172             return -1;
    173         }
    174 
    175         @Override
    176         public View getView(int position, View convertView, ViewGroup parent) {
    177             int topPanelCount = getTopPanelCount();
    178             return position < topPanelCount
    179                     ? mTopPanel : super.getView(position - topPanelCount, convertView, parent);
    180         }
    181 
    182         @Override
    183         public int getItemViewType(int position) {
    184             int numHeaders = getHeadersCount() + getTopPanelCount();
    185             if (getWrappedAdapter() != null && position >= numHeaders) {
    186                 int adjPosition = position - numHeaders;
    187                 int adapterCount = getWrappedAdapter().getCount();
    188                 if (adjPosition < adapterCount) {
    189                     return getWrappedAdapter().getItemViewType(adjPosition);
    190                 }
    191             }
    192 
    193             return AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER;
    194         }
    195     }
    196 }
    197