/* * Copyright (C) 2013 www.418log.org * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.ab.view.pullview; import android.content.Context; import android.graphics.Rect; import android.util.AttributeSet; import android.util.Log; import android.util.SparseIntArray; import android.view.View; // TODO: Auto-generated Javadoc /** * * Copyright (c) 2012 All rights reserved * 名称:AbMultiColumnBaseListView.java * 描述:The Class AbMultiColumnBaseListView. * @author zhaoqp * @date:2013-10-28 上午9:30:50 * @version v1.0 */ public class AbMultiColumnBaseListView extends AbMultiColumnAbsListView { /** The Constant TAG. */ @SuppressWarnings("unused") private static final String TAG = "MultiColumnListView"; /** The Constant DEFAULT_COLUMN_NUMBER. */ private static final int DEFAULT_COLUMN_NUMBER = 2; /** The m column number. */ private int mColumnNumber = 2; /** The m columns. */ private Column[] mColumns = null; /** The m fixed column. */ private Column mFixedColumn = null; // column for footers & headers. /** The m items. */ private SparseIntArray mItems = new SparseIntArray(); /** The m column padding left. */ private int mColumnPaddingLeft = 0; /** The m column padding right. */ private int mColumnPaddingRight = 0; /** * Instantiates a new ab multi column base list view. * * @param context the context */ public AbMultiColumnBaseListView(Context context) { super(context); init(null); } /** * Instantiates a new ab multi column base list view. * * @param context the context * @param attrs the attrs */ public AbMultiColumnBaseListView(Context context, AttributeSet attrs) { super(context, attrs); init(attrs); } /** The m frame rect. */ private Rect mFrameRect = new Rect(); /** * 初始化. * * @param attrs the attrs */ private void init(AttributeSet attrs) { getWindowVisibleDisplayFrame(mFrameRect); if (attrs == null) { mColumnNumber = (DEFAULT_COLUMN_NUMBER); // default column number is // 2. } else { int landColNumber = 3; int defColNumber = 3; if (mFrameRect.width() > mFrameRect.height() && landColNumber != -1) { mColumnNumber = (landColNumber); } else if (defColNumber != -1) { mColumnNumber = (defColNumber); } else { mColumnNumber = (DEFAULT_COLUMN_NUMBER); } mColumnPaddingLeft = 0; mColumnPaddingRight = 0; } mColumns = new Column[getColumnNumber()]; for (int i = 0; i < getColumnNumber(); ++i) mColumns[i] = new Column(i); mFixedColumn = new FixedColumn(); } // ///////////////////////////////////////////////////////////////////// // Override Methods... // ///////////////////////////////////////////////////////////////////// /** * 描述:TODO * @see com.ab.view.pullview.AbMultiColumnBaseAbsListView#onLayout(boolean, int, int, int, int) * @author: zhaoqp * @date:2013-9-4 下午4:06:33 * @version v1.0 */ @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); // TODO the adapter status may be changed. what should i do here... } /** The column width. */ private int columnWidth; /** * Gets the column width. * * @return the column width */ public int getColumnWidth() { return columnWidth; } /** * 描述:TODO * @see com.ab.view.pullview.AbMultiColumnAbsListView#onMeasure(int, int) * @author: zhaoqp * @date:2013-9-4 下午4:06:33 * @version v1.0 */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); columnWidth = (getMeasuredWidth() - mListPadding.left - mListPadding.right - mColumnPaddingLeft - mColumnPaddingRight) / getColumnNumber(); for (int index = 0; index < getColumnNumber(); ++index) { mColumns[index].mColumnWidth = columnWidth; mColumns[index].mColumnLeft = mListPadding.left + mColumnPaddingLeft + columnWidth * index; } mFixedColumn.mColumnLeft = mListPadding.left; mFixedColumn.mColumnWidth = getMeasuredWidth(); } /** * 描述:TODO * @see com.ab.view.pullview.AbMultiColumnAbsListView#onMeasureChild(android.view.View, int, int, int) * @author: zhaoqp * @date:2013-9-4 下午4:06:33 * @version v1.0 */ @Override protected void onMeasureChild(View child, int position, int widthMeasureSpec, int heightMeasureSpec) { if (isFixedView(child)) child.measure(widthMeasureSpec, heightMeasureSpec); else child.measure(MeasureSpec.EXACTLY | getColumnWidth(position), heightMeasureSpec); } /** * 描述:TODO * @see com.ab.view.pullview.AbMultiColumnBaseAbsListView#modifyFlingInitialVelocity(int) * @author: zhaoqp * @date:2013-9-4 下午4:06:33 * @version v1.0 */ @Override protected int modifyFlingInitialVelocity(int initialVelocity) { return initialVelocity / getColumnNumber(); } /** * 描述:TODO * @see com.ab.view.pullview.AbMultiColumnAbsListView#onItemAddedToList(int, boolean) * @author: zhaoqp * @date:2013-9-4 下午4:06:33 * @version v1.0 */ @Override protected void onItemAddedToList(int position, boolean flow) { super.onItemAddedToList(position, flow); if (isHeaderOrFooterPosition(position) == false) { Column col = getNextColumn(flow, position); mItems.append(position, col.getIndex()); } } /** * 描述:TODO * @see com.ab.view.pullview.AbMultiColumnBaseAbsListView#onLayoutSync(int) * @author: zhaoqp * @date:2013-9-4 下午4:06:33 * @version v1.0 */ @Override protected void onLayoutSync(int syncPos) { for (Column c : mColumns) { c.save(); } } /** * 描述:TODO * @see com.ab.view.pullview.AbMultiColumnBaseAbsListView#onLayoutSyncFinished(int) * @author: zhaoqp * @date:2013-9-4 下午4:06:33 * @version v1.0 */ @Override protected void onLayoutSyncFinished(int syncPos) { for (Column c : mColumns) { c.clear(); } } /** * 描述:TODO * @see com.ab.view.pullview.AbMultiColumnAbsListView#onAdjustChildViews(boolean) * @author: zhaoqp * @date:2013-9-4 下午4:06:33 * @version v1.0 */ @Override protected void onAdjustChildViews(boolean down) { int firstItem = getFirstVisiblePosition(); if (down == false && firstItem == 0) { final int firstColumnTop = mColumns[0].getTop(); for (Column c : mColumns) { final int top = c.getTop(); // align all column's top to 0's column. c.offsetTopAndBottom(firstColumnTop - top); } } super.onAdjustChildViews(down); } /** * 描述:TODO * @see com.ab.view.pullview.AbMultiColumnBaseAbsListView#getFillChildBottom() * @author: zhaoqp * @date:2013-9-4 下午4:06:33 * @version v1.0 */ @Override protected int getFillChildBottom() { // return smallest bottom value. // in order to determine fill down or not... (calculate below space) int result = Integer.MAX_VALUE; for (Column c : mColumns) { int bottom = c.getBottom(); result = result > bottom ? bottom : result; } return result; } /** * 描述:TODO * @see com.ab.view.pullview.AbMultiColumnBaseAbsListView#getFillChildTop() * @author: zhaoqp * @date:2013-9-4 下午4:06:33 * @version v1.0 */ @Override protected int getFillChildTop() { // find largest column. int result = Integer.MIN_VALUE; for (Column c : mColumns) { int top = c.getTop(); result = result < top ? top : result; } return result; } /** * 描述:TODO * @see com.ab.view.pullview.AbMultiColumnBaseAbsListView#getScrollChildBottom() * @author: zhaoqp * @date:2013-9-4 下午4:06:33 * @version v1.0 */ @Override protected int getScrollChildBottom() { // return largest bottom value. // for checking scrolling region... int result = Integer.MIN_VALUE; for (Column c : mColumns) { int bottom = c.getBottom(); result = result < bottom ? bottom : result; } return result; } /** * 描述:TODO * @see com.ab.view.pullview.AbMultiColumnBaseAbsListView#getScrollChildTop() * @author: zhaoqp * @date:2013-9-4 下午4:06:33 * @version v1.0 */ @Override protected int getScrollChildTop() { // find largest column. int result = Integer.MAX_VALUE; for (Column c : mColumns) { int top = c.getTop(); result = result > top ? top : result; } return result; } /** * 描述:TODO * @see com.ab.view.pullview.AbMultiColumnAbsListView#getItemLeft(int) * @author: zhaoqp * @date:2013-9-4 下午4:06:33 * @version v1.0 */ @Override protected int getItemLeft(int pos) { if (isHeaderOrFooterPosition(pos)) return mFixedColumn.getColumnLeft(); return getColumnLeft(pos); } /** * 描述:TODO * @see com.ab.view.pullview.AbMultiColumnAbsListView#getItemTop(int) * @author: zhaoqp * @date:2013-9-4 下午4:06:33 * @version v1.0 */ @Override protected int getItemTop(int pos) { if (isHeaderOrFooterPosition(pos)) return mFixedColumn.getBottom(); // footer view should be placed // below the last column. int colIndex = mItems.get(pos, -1); if (colIndex == -1) return getFillChildBottom(); return mColumns[colIndex].getBottom(); } /** * 描述:TODO * @see com.ab.view.pullview.AbMultiColumnAbsListView#getItemBottom(int) * @author: zhaoqp * @date:2013-9-4 下午4:06:33 * @version v1.0 */ @Override protected int getItemBottom(int pos) { if (isHeaderOrFooterPosition(pos)) return mFixedColumn.getTop(); // header view should be place above // the first column item. int colIndex = mItems.get(pos, -1); if (colIndex == -1) return getFillChildTop(); return mColumns[colIndex].getTop(); } // //////////////////////////////////////////////////////////////////////////// // Private Methods... // //////////////////////////////////////////////////////////////////////////// // flow If flow is true, align top edge to y. If false, align bottom edge to // y. /** * Gets the next column. * * @param flow the flow * @param position the position * @return the next column */ private Column getNextColumn(boolean flow, int position) { // we already have this item... int colIndex = mItems.get(position, -1); if (colIndex != -1) { return mColumns[colIndex]; } // adjust position (exclude headers...) position = Math.max(0, position - getHeaderViewsCount()); final int lastVisiblePos = Math.max(0, position); if (lastVisiblePos < getColumnNumber()) return mColumns[lastVisiblePos]; if (flow) { // find column which has the smallest bottom value. return gettBottomColumn(); } else { // find column which has the smallest top value. return getTopColumn(); } } /** * Checks if is header or footer position. * * @param pos the pos * @return true, if is header or footer position */ private boolean isHeaderOrFooterPosition(int pos) { int type = mAdapter.getItemViewType(pos); return type == ITEM_VIEW_TYPE_HEADER_OR_FOOTER; } /** * Gets the top column. * * @return the top column */ private Column getTopColumn() { Column result = mColumns[0]; for (Column c : mColumns) { result = result.getTop() > c.getTop() ? c : result; } return result; } /** * Gets the t bottom column. * * @return the t bottom column */ private Column gettBottomColumn() { Column result = mColumns[0]; for (Column c : mColumns) { result = result.getBottom() > c.getBottom() ? c : result; } if (DEBUG) Log.d("Column", "get Shortest Bottom Column: " + result.getIndex()); return result; } /** * Gets the column left. * * @param pos the pos * @return the column left */ private int getColumnLeft(int pos) { int colIndex = mItems.get(pos, -1); if (colIndex == -1) return 0; return mColumns[colIndex].getColumnLeft(); } /** * Gets the column width. * * @param pos the pos * @return the column width */ private int getColumnWidth(int pos) { int colIndex = mItems.get(pos, -1); if (colIndex == -1) return 0; return mColumns[colIndex].getColumnWidth(); } // ///////////////////////////////////////////////////////////// // Inner Class. // ///////////////////////////////////////////////////////////// /** * Gets the column number. * * @return the column number */ public int getColumnNumber() { return mColumnNumber; } /** * The Class Column. */ private class Column { /** The m index. */ private int mIndex; /** The m column width. */ private int mColumnWidth; /** The m column left. */ private int mColumnLeft; /** The m synched top. */ private int mSynchedTop = 0; /** The m synched bottom. */ private int mSynchedBottom = 0; // TODO is it ok to use item position info to identify item?? /** * Instantiates a new column. * * @param index the index */ public Column(int index) { mIndex = index; } /** * Gets the column left. * * @return the column left */ public int getColumnLeft() { return mColumnLeft; } /** * Gets the column width. * * @return the column width */ public int getColumnWidth() { return mColumnWidth; } /** * Gets the index. * * @return the index */ public int getIndex() { return mIndex; } /** * Gets the bottom. * * @return the bottom */ public int getBottom() { // find biggest value. int bottom = Integer.MIN_VALUE; int childCount = getChildCount(); for (int index = 0; index < childCount; ++index) { View v = getChildAt(index); if (v.getLeft() != mColumnLeft && isFixedView(v) == false) continue; bottom = bottom < v.getBottom() ? v.getBottom() : bottom; } if (bottom == Integer.MIN_VALUE) return mSynchedBottom; // no child for this column.. return bottom; } /** * Offset top and bottom. * * @param offset the offset */ public void offsetTopAndBottom(int offset) { if (offset == 0) return; // find biggest value. int childCount = getChildCount(); for (int index = 0; index < childCount; ++index) { View v = getChildAt(index); if (v.getLeft() != mColumnLeft && isFixedView(v) == false) continue; v.offsetTopAndBottom(offset); } } /** * Gets the top. * * @return the top */ public int getTop() { // find smallest value. int top = Integer.MAX_VALUE; int childCount = getChildCount(); for (int index = 0; index < childCount; ++index) { View v = getChildAt(index); if (v.getLeft() != mColumnLeft && isFixedView(v) == false) continue; top = top > v.getTop() ? v.getTop() : top; } if (top == Integer.MAX_VALUE) return mSynchedTop; // no child for this column. just return // saved sync top.. return top; } /** * Save. */ public void save() { mSynchedTop = 0; mSynchedBottom = getTop(); // getBottom(); } /** * Clear. */ public void clear() { mSynchedTop = 0; mSynchedBottom = 0; } }// end of inner class Column /** * The Class FixedColumn. */ private class FixedColumn extends Column { /** * Instantiates a new fixed column. */ public FixedColumn() { super(Integer.MAX_VALUE); } /** * 描述:TODO * @see com.ab.view.pullview.AbMultiColumnBaseListView.Column#getBottom() * @author: zhaoqp * @date:2013-9-4 下午4:06:34 * @version v1.0 */ @Override public int getBottom() { return getScrollChildBottom(); } /** * 描述:TODO * @see com.ab.view.pullview.AbMultiColumnBaseListView.Column#getTop() * @author: zhaoqp * @date:2013-9-4 下午4:06:34 * @version v1.0 */ @Override public int getTop() { return getScrollChildTop(); } }// end of class }// end of class