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 WatchHeaderListView(Context context, AttributeSet attrs)34 public WatchHeaderListView(Context context, AttributeSet attrs) { 35 super(context, attrs); 36 } 37 WatchHeaderListView(Context context, AttributeSet attrs, int defStyleAttr)38 public WatchHeaderListView(Context context, AttributeSet attrs, int defStyleAttr) { 39 super(context, attrs, defStyleAttr); 40 } 41 WatchHeaderListView( Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)42 public WatchHeaderListView( 43 Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 44 super(context, attrs, defStyleAttr, defStyleRes); 45 } 46 47 @Override wrapHeaderListAdapterInternal( ArrayList<ListView.FixedViewInfo> headerViewInfos, ArrayList<ListView.FixedViewInfo> footerViewInfos, ListAdapter adapter)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 addView(View child, ViewGroup.LayoutParams params)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 setTopPanel(View v)64 public void setTopPanel(View v) { 65 mTopPanel = v; 66 wrapAdapterIfNecessary(); 67 } 68 69 @Override setAdapter(ListAdapter adapter)70 public void setAdapter(ListAdapter adapter) { 71 super.setAdapter(adapter); 72 wrapAdapterIfNecessary(); 73 } 74 75 @Override findViewTraversal(@dRes int id)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 findViewWithTagTraversal(Object tag)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 findViewByPredicateTraversal( Predicate<View> predicate, View childToSkip)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 getHeaderViewsCount()105 public int getHeaderViewsCount() { 106 return mTopPanel == null ? super.getHeaderViewsCount() 107 : super.getHeaderViewsCount() + (mTopPanel.getVisibility() == GONE ? 0 : 1); 108 } 109 wrapAdapterIfNecessary()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 WatchHeaderListAdapter( ArrayList<ListView.FixedViewInfo> headerViewInfos, ArrayList<ListView.FixedViewInfo> footerViewInfos, ListAdapter adapter)125 public WatchHeaderListAdapter( 126 ArrayList<ListView.FixedViewInfo> headerViewInfos, 127 ArrayList<ListView.FixedViewInfo> footerViewInfos, 128 ListAdapter adapter) { 129 super(headerViewInfos, footerViewInfos, adapter); 130 } 131 setTopPanel(View v)132 public void setTopPanel(View v) { 133 mTopPanel = v; 134 } 135 getTopPanelCount()136 private int getTopPanelCount() { 137 return (mTopPanel == null || mTopPanel.getVisibility() == GONE) ? 0 : 1; 138 } 139 140 @Override getCount()141 public int getCount() { 142 return super.getCount() + getTopPanelCount(); 143 } 144 145 @Override areAllItemsEnabled()146 public boolean areAllItemsEnabled() { 147 return getTopPanelCount() == 0 && super.areAllItemsEnabled(); 148 } 149 150 @Override isEnabled(int position)151 public boolean isEnabled(int position) { 152 int topPanelCount = getTopPanelCount(); 153 return position < topPanelCount ? false : super.isEnabled(position - topPanelCount); 154 } 155 156 @Override getItem(int position)157 public Object getItem(int position) { 158 int topPanelCount = getTopPanelCount(); 159 return position < topPanelCount ? null : super.getItem(position - topPanelCount); 160 } 161 162 @Override getItemId(int position)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 getView(int position, View convertView, ViewGroup parent)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 getItemViewType(int position)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