1 /* 2 * Copyright (C) 2010 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.contacts.widget; 17 18 import com.android.common.widget.CompositeCursorAdapter; 19 20 import android.content.Context; 21 import android.view.View; 22 import android.view.ViewGroup; 23 24 /** 25 * A subclass of {@link CompositeCursorAdapter} that manages pinned partition headers. 26 */ 27 public abstract class PinnedHeaderListAdapter extends CompositeCursorAdapter 28 implements PinnedHeaderListView.PinnedHeaderAdapter { 29 30 public static final int PARTITION_HEADER_TYPE = 0; 31 32 private boolean mPinnedPartitionHeadersEnabled; 33 private boolean mHeaderVisibility[]; 34 PinnedHeaderListAdapter(Context context)35 public PinnedHeaderListAdapter(Context context) { 36 super(context); 37 } 38 PinnedHeaderListAdapter(Context context, int initialCapacity)39 public PinnedHeaderListAdapter(Context context, int initialCapacity) { 40 super(context, initialCapacity); 41 } 42 getPinnedPartitionHeadersEnabled()43 public boolean getPinnedPartitionHeadersEnabled() { 44 return mPinnedPartitionHeadersEnabled; 45 } 46 setPinnedPartitionHeadersEnabled(boolean flag)47 public void setPinnedPartitionHeadersEnabled(boolean flag) { 48 this.mPinnedPartitionHeadersEnabled = flag; 49 } 50 getPinnedHeaderCount()51 public int getPinnedHeaderCount() { 52 if (mPinnedPartitionHeadersEnabled) { 53 return getPartitionCount(); 54 } else { 55 return 0; 56 } 57 } 58 isPinnedPartitionHeaderVisible(int partition)59 protected boolean isPinnedPartitionHeaderVisible(int partition) { 60 return mPinnedPartitionHeadersEnabled && hasHeader(partition) 61 && !isPartitionEmpty(partition); 62 } 63 64 /** 65 * The default implementation creates the same type of view as a normal 66 * partition header. 67 */ getPinnedHeaderView(int partition, View convertView, ViewGroup parent)68 public View getPinnedHeaderView(int partition, View convertView, ViewGroup parent) { 69 if (hasHeader(partition)) { 70 View view = null; 71 if (convertView != null) { 72 Integer headerType = (Integer)convertView.getTag(); 73 if (headerType != null && headerType == PARTITION_HEADER_TYPE) { 74 view = convertView; 75 } 76 } 77 if (view == null) { 78 view = newHeaderView(getContext(), partition, null, parent); 79 view.setTag(PARTITION_HEADER_TYPE); 80 view.setFocusable(false); 81 view.setEnabled(false); 82 } 83 bindHeaderView(view, partition, getCursor(partition)); 84 return view; 85 } else { 86 return null; 87 } 88 } 89 configurePinnedHeaders(PinnedHeaderListView listView)90 public void configurePinnedHeaders(PinnedHeaderListView listView) { 91 if (!mPinnedPartitionHeadersEnabled) { 92 return; 93 } 94 95 int size = getPartitionCount(); 96 97 // Cache visibility bits, because we will need them several times later on 98 if (mHeaderVisibility == null || mHeaderVisibility.length != size) { 99 mHeaderVisibility = new boolean[size]; 100 } 101 for (int i = 0; i < size; i++) { 102 boolean visible = isPinnedPartitionHeaderVisible(i); 103 mHeaderVisibility[i] = visible; 104 if (!visible) { 105 listView.setHeaderInvisible(i, true); 106 } 107 } 108 109 int headerViewsCount = listView.getHeaderViewsCount(); 110 111 // Starting at the top, find and pin headers for partitions preceding the visible one(s) 112 int maxTopHeader = -1; 113 int topHeaderHeight = 0; 114 for (int i = 0; i < size; i++) { 115 if (mHeaderVisibility[i]) { 116 int position = listView.getPositionAt(topHeaderHeight) - headerViewsCount; 117 int partition = getPartitionForPosition(position); 118 if (i > partition) { 119 break; 120 } 121 122 listView.setHeaderPinnedAtTop(i, topHeaderHeight, false); 123 topHeaderHeight += listView.getPinnedHeaderHeight(i); 124 maxTopHeader = i; 125 } 126 } 127 128 // Starting at the bottom, find and pin headers for partitions following the visible one(s) 129 int maxBottomHeader = size; 130 int bottomHeaderHeight = 0; 131 int listHeight = listView.getHeight(); 132 for (int i = size; --i > maxTopHeader;) { 133 if (mHeaderVisibility[i]) { 134 int position = listView.getPositionAt(listHeight - bottomHeaderHeight) 135 - headerViewsCount; 136 if (position < 0) { 137 break; 138 } 139 140 int partition = getPartitionForPosition(position - 1); 141 if (partition == -1 || i <= partition) { 142 break; 143 } 144 145 int height = listView.getPinnedHeaderHeight(i); 146 bottomHeaderHeight += height; 147 // Animate the header only if the partition is completely invisible below 148 // the bottom of the view 149 int firstPositionForPartition = getPositionForPartition(i); 150 boolean animate = position < firstPositionForPartition; 151 listView.setHeaderPinnedAtBottom(i, listHeight - bottomHeaderHeight, animate); 152 maxBottomHeader = i; 153 } 154 } 155 156 // Headers in between the top-pinned and bottom-pinned should be hidden 157 for (int i = maxTopHeader + 1; i < maxBottomHeader; i++) { 158 if (mHeaderVisibility[i]) { 159 listView.setHeaderInvisible(i, isPartitionEmpty(i)); 160 } 161 } 162 } 163 164 public int getScrollPositionForHeader(int viewIndex) { 165 return getPositionForPartition(viewIndex); 166 } 167 } 168