/*
* Copyright (C) 2012 www.amsoft.cn
*
* 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 java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.database.DataSetObserver;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import com.ab.util.AbLogUtil;
// TODO: Auto-generated Javadoc
/**
* The Class AbMultiColumnListView.
*/
public class AbMultiColumnListView extends ScrollView{
/** 每一列的宽度. */
private int columnWidth;
/** 当前第一列的高度. */
private int firstColumnHeight;
/** 当前第二列的高度. */
private int secondColumnHeight;
/** 当前第三列的高度. */
private int thirdColumnHeight;
/** 是否已加载过一次layout,这里onLayout中的初始化只需加载一次. */
private boolean loadOnce;
/** 布局的高度。. */
private static int scrollViewHeight;
/** 第一列的布局. */
private LinearLayout firstColumn;
/** 第二列的布局. */
private LinearLayout secondColumn;
/** 第三列的布局. */
private LinearLayout thirdColumn;
/** 直接子布局. */
private LinearLayout scrollLayout;
/** Adapter. */
private AbMultiColumnListAdapter mAdapter = null;
/** Adapter改变的监听器. */
private AdapterDataSetObserver mDataSetObserver;
/** 已加载的View. */
private List<AbViewInfo> mItems = null;
/** 已加载的View. */
private OnScrollListener mOnScrollListener = null;
/** 可释放图片资源的id. */
private int[] mReleaseImageResIds;
/**
* Instantiates a new ab multi column list view.
*
* @param context the context
*/
public AbMultiColumnListView(Context context) {
this(context,null);
}
/**
* Instantiates a new ab multi column list view.
*
* @param context the context
* @param attrs the attrs
*/
public AbMultiColumnListView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
/**
* Instantiates a new ab multi column list view.
*
* @param context the context
* @param attrs the attrs
* @param defStyle the def style
*/
public AbMultiColumnListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
scrollLayout = new LinearLayout(context);
scrollLayout.setOrientation(LinearLayout.HORIZONTAL);
firstColumn = new LinearLayout(context);
firstColumn.setOrientation(LinearLayout.VERTICAL);
secondColumn = new LinearLayout(context);
secondColumn.setOrientation(LinearLayout.VERTICAL);
thirdColumn = new LinearLayout(context);
thirdColumn.setOrientation(LinearLayout.VERTICAL);
scrollLayout.addView(firstColumn, new LinearLayout.LayoutParams(0,LayoutParams.WRAP_CONTENT,1));
scrollLayout.addView(secondColumn, new LinearLayout.LayoutParams(0,LayoutParams.WRAP_CONTENT,1));
scrollLayout.addView(thirdColumn, new LinearLayout.LayoutParams(0,LayoutParams.WRAP_CONTENT,1));
this.addView(scrollLayout, new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.WRAP_CONTENT));
mItems = new ArrayList<AbViewInfo>();
}
/**
* Gets the adapter.
*
* @return the adapter
*/
public AbMultiColumnListAdapter getAdapter() {
return mAdapter;
}
/**
* Sets the adapter.
*
* @param adapter the new adapter
*/
public void setAdapter(AbMultiColumnListAdapter adapter) {
this.mAdapter = adapter;
if (mAdapter != null && mDataSetObserver != null) {
mAdapter.unregisterDataSetObserver(mDataSetObserver);
}
if (mAdapter != null) {
mDataSetObserver = new AdapterDataSetObserver();
mAdapter.registerDataSetObserver(mDataSetObserver);
}
layoutChildren();
}
/**
* 进行一些关键性的初始化操作,获取AbMultiColumnListView的高度,
* 以及得到第一列的宽度值。并在这里开始加载第一页的图片。.
*
* @param changed the changed
* @param l the l
* @param t the t
* @param r the r
* @param b the b
*/
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
//AbLogUtil.d(AbMultiColumnListView.class, "onLayout");
if (changed && !loadOnce) {
scrollViewHeight = getHeight();
scrollLayout = (LinearLayout)getChildAt(0);
columnWidth = firstColumn.getWidth();
loadOnce = true;
}
}
/**
* Layout children.
*/
protected void layoutChildren() {
AbLogUtil.d(AbMultiColumnListView.class, "layoutChildren");
firstColumn.removeAllViews();
secondColumn.removeAllViews();
thirdColumn.removeAllViews();
mItems.clear();
firstColumnHeight = 0;
secondColumnHeight = 0;
thirdColumnHeight = 0;
if(mAdapter != null){
int mItemCount = mAdapter.getCount();
for (int i = 0; i < mItemCount; i++) {
AbViewInfo viewInfo = mAdapter.getView(i, null, null);
viewInfo.setVisible(View.VISIBLE);
findColumnToAdd(viewInfo);
mItems.add(viewInfo);
}
}
}
/**
* Adds the children.
*/
protected void addChildren() {
AbLogUtil.d(AbMultiColumnListView.class, "addChildren");
if(mAdapter != null){
int count = mAdapter.getCount();
if(count > mItems.size()){
for (int i = mItems.size(); i < count; i++) {
AbViewInfo viewInfo = mAdapter.getView(i, null, null);
viewInfo.setVisible(View.VISIBLE);
findColumnToAdd(viewInfo);
mItems.add(viewInfo);
}
}
}
}
/**
* 找到此时应该添加View的一列。原则就是对三列的高度进行判断,
* 当前高度最小的一列就是应该添加的一列。.
*
* @param viewInfo the view info
*/
private void findColumnToAdd(AbViewInfo viewInfo) {
int width = viewInfo.getWidth();
int height = viewInfo.getHeight();
int scaledHeight = 0;
double ratio = width / (columnWidth * 1.0);
scaledHeight = (int) (height / ratio);
View view = viewInfo.getView();
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
columnWidth, scaledHeight);
if (firstColumnHeight <= secondColumnHeight) {
if (firstColumnHeight <= thirdColumnHeight) {
viewInfo.setTop(firstColumnHeight);
firstColumnHeight += scaledHeight;
viewInfo.setBottom(firstColumnHeight);
firstColumn.addView(view,params);
}else{
viewInfo.setTop(thirdColumnHeight);
thirdColumnHeight += scaledHeight;
viewInfo.setBottom(thirdColumnHeight);
thirdColumn.addView(view,params);
}
} else {
if (secondColumnHeight <= thirdColumnHeight) {
viewInfo.setTop(secondColumnHeight);
secondColumnHeight += scaledHeight;
viewInfo.setBottom(secondColumnHeight);
secondColumn.addView(view,params);
}else{
viewInfo.setTop(thirdColumnHeight);
thirdColumnHeight += scaledHeight;
viewInfo.setBottom(thirdColumnHeight);
thirdColumn.addView(view,params);
}
}
}
/* (non-Javadoc)
* @see android.view.View#onScrollChanged(int, int, int, int)
*/
@Override
protected void onScrollChanged(int x, int y, int oldx, int oldy) {
super.onScrollChanged(x, y, oldx, oldy);
//将不可见区域的资源释放
AbViewInfo viewInfo = null;
for(int i=0;i<mItems.size();i++){
viewInfo = mItems.get(i);
if(!checkVisibility(i)){
if(viewInfo.getVisible()==View.VISIBLE){
viewInfo.setVisible(View.INVISIBLE);
ImageView imageView = null;
for(int id:mReleaseImageResIds){
imageView = (ImageView) viewInfo.getView().findViewById(id);
imageView.setImageBitmap(null);
}
}
}else{
try {
if(viewInfo.getVisible()==View.INVISIBLE){
//重新getView
viewInfo = mAdapter.getView(i, viewInfo, null);
viewInfo.setVisible(View.VISIBLE);
}
} catch (Exception e) {
}
}
}
mOnScrollListener.onScrollChanged(x, y, oldx, oldy);
}
/**
* 遍历List中的每个View,对可见性进行检查.
*
* @param position the position
* @return true, if successful
*/
public boolean checkVisibility(int position) {
AbViewInfo viewInfo = mItems.get(position);
int borderTop = viewInfo.getTop();
int borderBottom = viewInfo.getBottom();
if (borderBottom > getScrollY()
&& borderTop < getScrollY() + scrollViewHeight) {
return true;
} else {
return false;
}
}
/**
* An asynchronous update interface for receiving notifications
* about AdapterDataSet information as the AdapterDataSet is constructed.
*/
class AdapterDataSetObserver extends DataSetObserver {
/* (non-Javadoc)
* @see android.database.DataSetObserver#onChanged()
*/
@Override
public void onChanged() {
AbLogUtil.d(AbMultiColumnListView.class, "onChanged");
//判断是刷新还是添加
int count = mAdapter.getCount();
if(count > mItems.size()){
//添加
addChildren();
}else{
//刷新
layoutChildren();
}
super.onChanged();
}
/* (non-Javadoc)
* @see android.database.DataSetObserver#onInvalidated()
*/
@Override
public void onInvalidated() {
super.onInvalidated();
}
}
/**
* Gets the on scroll listener.
*
* @return the on scroll listener
*/
public OnScrollListener getOnScrollListener() {
return mOnScrollListener;
}
/**
* Sets the on scroll listener.
*
* @param onScrollListener the new on scroll listener
*/
public void setOnScrollListener(OnScrollListener onScrollListener) {
this.mOnScrollListener = onScrollListener;
}
/**
* Gets the release image res ids.
*
* @return the release image res ids
*/
public int[] getReleaseImageResIds() {
return mReleaseImageResIds;
}
/**
* Sets the release image res ids.
*
* @param releaseImageResIds the new release image res ids
*/
public void setReleaseImageResIds(int[] releaseImageResIds) {
this.mReleaseImageResIds = releaseImageResIds;
}
/**
* The listener interface for receiving onScroll events.
* The class that is interested in processing a onScroll
* event implements this interface, and the object created
* with that class is registered with a component using the
* component's <code>addOnScrollListener<code> method. When
* the onScroll event occurs, that object's appropriate
* method is invoked.
*
* @see OnScrollEvent
*/
public interface OnScrollListener {
/**
* On scroll changed.
*
* @param x the x
* @param y the y
* @param oldx the oldx
* @param oldy the oldy
*/
void onScrollChanged(int x, int y, int oldx, int oldy);
}
}