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