先看效果图.![下拉刷新]!
package zhuxiaohao.com.cn.slidingtabforlistview.custom;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.ScrollView;
/**
* Project Name:zhuxiaohao.com.cn.slidingtabforlistview.custom
* File Name: SlidingTabForListview
* Date:15/8/7上午12:3108
* blog:http://blog.csdn.net/qq718799510?viewmode=contents
* Copyright (c) 2015, zhuxiaohao All Rights Reserved.
*/
public class CustomScrollView extends ScrollView {
private Callbacks mCallbacks;
public CustomScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
if (mCallbacks != null) {
mCallbacks.onScrollChanged(t);
}
}
@Override
public boolean onTouchEvent(android.view.MotionEvent ev) {
if (mCallbacks != null) {
switch (ev.getActionMasked()) {
case android.view.MotionEvent.ACTION_DOWN:
mCallbacks.onDownMotionEvent();
break;
case android.view.MotionEvent.ACTION_UP:
case android.view.MotionEvent.ACTION_CANCEL:
mCallbacks.onUpOrCancelMotionEvent();
break;
}
}
return super.onTouchEvent(ev);
}
@Override
public int computeVerticalScrollRange() {
return super.computeVerticalScrollRange();
}
public void setCallbacks(Callbacks listener) {
mCallbacks = listener;
}
/**
* Callback is interface
*/
public static interface Callbacks {
public void onScrollChanged(int scrollY);
public void onDownMotionEvent();
public void onUpOrCancelMotionEvent();
}
}
这里重写 scrollview 监听 scroll事件时候置顶 Tab.
package zhuxiaohao.com.cn.slidingtabforlistview.custom;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.util.AttributeSet;
import android.widget.LinearLayout;
/**
* Project Name:zhuxiaohao.com.cn.slidingtabforlistview.custom
* File Name: SlidingTabForListview
* Date:15/8/7上午12:2608
* blog:http://blog.csdn.net/qq718799510?viewmode=contents
* Copyright (c) 2015, zhuxiaohao All Rights Reserved.
*/
public class CustomLinearLayout extends LinearLayout {
private static final int[] R_styleable_LinearLayout = new int[] {
/* 0 */ android.R.attr.divider,
/* 1 */ android.R.attr.measureWithLargestChild,
/* 2 */ android.R.attr.showDividers,
/* 3 */ android.R.attr.dividerPadding,
};
private static final int LinearLayout_divider = 0;
private static final int LinearLayout_measureWithLargestChild = 1;
private static final int LinearLayout_showDividers = 2;
private static final int LinearLayout_dividerPadding = 3;
/**
* Don't show any dividers.
*/
public static final int SHOW_DIVIDER_NONE = 0;
/**
* Show a divider at the beginning of the group.
*/
public static final int SHOW_DIVIDER_BEGINNING = 1;
/**
* Show dividers between each item in the group.
*/
public static final int SHOW_DIVIDER_MIDDLE = 2;
/**
* Show a divider at the end of the group.
*/
public static final int SHOW_DIVIDER_END = 4;
private static final boolean IS_HONEYCOMB = VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB;
private Drawable mDivider;
protected int mDividerWidth;
protected int mDividerHeight;
private int mShowDividers;
private int mDividerPadding;
private boolean mClipDivider;
private boolean mUseLargestChild;
public CustomLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs, /*com.android.internal.R.styleable.*/R_styleable_LinearLayout);
setDividerDrawable(a.getDrawable(/*com.android.internal.R.styleable.*/LinearLayout_divider));
mShowDividers = a.getInt(/*com.android.internal.R.styleable.*/LinearLayout_showDividers, SHOW_DIVIDER_NONE);
mDividerPadding = a.getDimensionPixelSize(/*com.android.internal.R.styleable.*/LinearLayout_dividerPadding, 0);
mUseLargestChild = a.getBoolean(/*com.android.internal.R.styleable.*/LinearLayout_measureWithLargestChild, false);
a.recycle();
}
/**
* Set how dividers should be shown between items in this layout
*
* @param showDividers One or more of {@link #SHOW_DIVIDER_BEGINNING},
* {@link #SHOW_DIVIDER_MIDDLE}, or {@link #SHOW_DIVIDER_END},
* or {@link #SHOW_DIVIDER_NONE} to show no dividers.
*/
public void setShowDividers(int showDividers) {
if (showDividers != mShowDividers) {
requestLayout();
invalidate(); //XXX This is required if you are toggling a divider off
}
mShowDividers = showDividers;
}
/**
* @return A flag set indicating how dividers should be shown around items.
* @see #setShowDividers(int)
*/
public int getShowDividers() {
return mShowDividers;
}
/**
* Set a drawable to be used as a divider between items.
* @param divider Drawable that will divide each item.
* @see #setShowDividers(int)
*/
public void setDividerDrawable(Drawable divider) {
if (divider == mDivider) {
return;
}
mDivider = divider;
mClipDivider = divider instanceof android.graphics.drawable.ColorDrawable;
if (divider != null) {
mDividerWidth = divider.getIntrinsicWidth();
mDividerHeight = divider.getIntrinsicHeight();
} else {
mDividerWidth = 0;
mDividerHeight = 0;
}
setWillNotDraw(divider == null);
requestLayout();
}
/**
* Set padding displayed on both ends of dividers.
*
* @param padding Padding value in pixels that will be applied to each end
*
* @see #setShowDividers(int)
* @see #setDividerDrawable(Drawable)
* @see #getDividerPadding()
*/
public void setDividerPadding(int padding) {
mDividerPadding = padding;
}
/**
* Get the padding size used to inset dividers in pixels
*
* @see #setShowDividers(int)
* @see #setDividerDrawable(Drawable)
* @see #setDividerPadding(int)
*/
public int getDividerPadding() {
return mDividerPadding;
}
/**
* Get the width of the current divider drawable.
*
* @hide Used internally by framework.
*/
public int getDividerWidth() {
return mDividerWidth;
}
@Override
protected void measureChildWithMargins(android.view.View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
final int index = indexOfChild(child);
final int orientation = getOrientation();
final LayoutParams params = (LayoutParams) child.getLayoutParams();
if (hasDividerBeforeChildAt(index)) {
if (orientation == VERTICAL) {
//Account for the divider by pushing everything up
params.topMargin = mDividerHeight;
} else {
//Account for the divider by pushing everything left
params.leftMargin = mDividerWidth;
}
}
final int count = getChildCount();
if (index == count - 1) {
if (hasDividerBeforeChildAt(count)) {
if (orientation == VERTICAL) {
params.bottomMargin = mDividerHeight;
} else {
params.rightMargin = mDividerWidth;
}
}
}
super.measureChildWithMargins(child, parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed);
}
@Override
protected void onDraw(android.graphics.Canvas canvas) {
if (mDivider != null) {
if (getOrientation() == VERTICAL) {
drawDividersVertical(canvas);
} else {
drawDividersHorizontal(canvas);
}
}
super.onDraw(canvas);
}
void drawDividersVertical(android.graphics.Canvas canvas) {
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final android.view.View child = getChildAt(i);
if (child != null && child.getVisibility() != GONE) {
if (hasDividerBeforeChildAt(i)) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
final int top = child.getTop() - lp.topMargin/* - mDividerHeight*/;
drawHorizontalDivider(canvas, top);
}
}
}
if (hasDividerBeforeChildAt(count)) {
final android.view.View child = getChildAt(count - 1);
int bottom = 0;
if (child == null) {
bottom = getHeight() - getPaddingBottom() - mDividerHeight;
} else {
//final LayoutParams lp = (LayoutParams) child.getLayoutParams();
bottom = child.getBottom()/* + lp.bottomMargin*/;
}
drawHorizontalDivider(canvas, bottom);
}
}
void drawDividersHorizontal(android.graphics.Canvas canvas) {
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final android.view.View child = getChildAt(i);
if (child != null && child.getVisibility() != GONE) {
if (hasDividerBeforeChildAt(i)) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
final int left = child.getLeft() - lp.leftMargin/* - mDividerWidth*/;
drawVerticalDivider(canvas, left);
}
}
}
if (hasDividerBeforeChildAt(count)) {
final android.view.View child = getChildAt(count - 1);
int right = 0;
if (child == null) {
right = getWidth() - getPaddingRight() - mDividerWidth;
} else {
//final LayoutParams lp = (LayoutParams) child.getLayoutParams();
right = child.getRight()/* + lp.rightMargin*/;
}
drawVerticalDivider(canvas, right);
}
}
void drawHorizontalDivider(android.graphics.Canvas canvas, int top) {
if(mClipDivider && !IS_HONEYCOMB) {
canvas.save();
canvas.clipRect(getPaddingLeft() + mDividerPadding, top,
getWidth() - getPaddingRight() - mDividerPadding, top + mDividerHeight);
mDivider.draw(canvas);
canvas.restore();
} else {
mDivider.setBounds(getPaddingLeft() + mDividerPadding, top,
getWidth() - getPaddingRight() - mDividerPadding, top + mDividerHeight);
mDivider.draw(canvas);
}
}
void drawVerticalDivider(android.graphics.Canvas canvas, int left) {
if(mClipDivider && !IS_HONEYCOMB) {
canvas.save();
canvas.clipRect(left, getPaddingTop() + mDividerPadding,
left + mDividerWidth, getHeight() - getPaddingBottom() - mDividerPadding);
mDivider.draw(canvas);
canvas.restore();
} else {
mDivider.setBounds(left, getPaddingTop() + mDividerPadding,
left + mDividerWidth, getHeight() - getPaddingBottom() - mDividerPadding);
mDivider.draw(canvas);
}
}
/**
* Determines where to position dividers between children.
*
* @param childIndex Index of child to check for preceding divider
* @return true if there should be a divider before the child at childIndex
* @hide Pending API consideration. Currently only used internally by the system.
*/
protected boolean hasDividerBeforeChildAt(int childIndex) {
if (childIndex == 0) {
return (mShowDividers & SHOW_DIVIDER_BEGINNING) != 0;
} else if (childIndex == getChildCount()) {
return (mShowDividers & SHOW_DIVIDER_END) != 0;
} else if ((mShowDividers & SHOW_DIVIDER_MIDDLE) != 0) {
boolean hasVisibleViewBefore = false;
for (int i = childIndex - 1; i >= 0; i--) {
if (getChildAt(i).getVisibility() != GONE) {
hasVisibleViewBefore = true;
break;
}
}
return hasVisibleViewBefore;
}
return false;
}
/**
* When true, all children with a weight will be considered having
* the minimum size of the largest child. If false, all children are
* measured normally.
*
* @return True to measure children with a weight using the minimum
* size of the largest child, false otherwise.
*
* @attr ref android.R.styleable#LinearLayout_measureWithLargestChild
*/
public boolean isMeasureWithLargestChildEnabled() {
return mUseLargestChild;
}
/**
* When set to true, all children with a weight will be considered having
* the minimum size of the largest child. If false, all children are
* measured normally.
*
* Disabled by default.
*
* @param enabled True to measure children with a weight using the
* minimum size of the largest child, false otherwise.
*
* @attr ref android.R.styleable#LinearLayout_measureWithLargestChild
*/
public void setMeasureWithLargestChildEnabled(boolean enabled) {
mUseLargestChild = enabled;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (mUseLargestChild) {
final int orientation = getOrientation();
switch (orientation) {
case HORIZONTAL:
useLargestChildHorizontal();
break;
case VERTICAL:
useLargestChildVertical();
break;
}
}
}
private void useLargestChildHorizontal() {
final int childCount = getChildCount();
// Find largest child width
int largestChildWidth = 0;
for (int i = 0; i < childCount; i++) {
final android.view.View child = getChildAt(i);
largestChildWidth = Math.max(child.getMeasuredWidth(), largestChildWidth);
}
int totalWidth = 0;
// Re-measure childs
for (int i = 0; i < childCount; i++) {
final android.view.View child = getChildAt(i);
if (child == null || child.getVisibility() == android.view.View.GONE) {
continue;
}
final LayoutParams lp =
(LayoutParams) child.getLayoutParams();
float childExtra = lp.weight;
if (childExtra > 0) {
child.measure(
MeasureSpec.makeMeasureSpec(largestChildWidth,
MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(child.getMeasuredHeight(),
MeasureSpec.EXACTLY));
totalWidth += largestChildWidth;
} else {
totalWidth += child.getMeasuredWidth();
}
totalWidth += lp.leftMargin + lp.rightMargin;
}
totalWidth += getPaddingLeft() + getPaddingRight();
setMeasuredDimension(totalWidth, getMeasuredHeight());
}
private void useLargestChildVertical() {
final int childCount = getChildCount();
// Find largest child width
int largestChildHeight = 0;
for (int i = 0; i < childCount; i++) {
final android.view.View child = getChildAt(i);
largestChildHeight = Math.max(child.getMeasuredHeight(), largestChildHeight);
}
int totalHeight = 0;
// Re-measure childs
for (int i = 0; i < childCount; i++) {
final android.view.View child = getChildAt(i);
if (child == null || child.getVisibility() == android.view.View.GONE) {
continue;
}
final LayoutParams lp =
(LayoutParams) child.getLayoutParams();
float childExtra = lp.weight;
if (childExtra > 0) {
child.measure(
MeasureSpec.makeMeasureSpec(child.getMeasuredWidth(),
MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(largestChildHeight,
MeasureSpec.EXACTLY));
totalHeight += largestChildHeight;
} else {
totalHeight += child.getMeasuredHeight();
}
totalHeight += lp.leftMargin + lp.rightMargin;
}
totalHeight += getPaddingLeft() + getPaddingRight();
setMeasuredDimension(getMeasuredWidth(), totalHeight);
}
}
重写 linearlayout 让他具备 listview功能.
package zhuxiaohao.com.cn.slidingtabforlistview.custom;
import android.content.Context;
import android.content.res.TypedArray;
import android.database.DataSetObserver;
import android.util.AttributeSet;
import android.view.SoundEffectConstants;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ListAdapter;
/**
* Project Name:zhuxiaohao.com.cn.slidingtabforlistview.custom
* File Name: SlidingTabForListview
* Date:15/8/7上午12:2908
* blog:http://blog.csdn.net/qq718799510?viewmode=contents
* Copyright (c) 2015, zhuxiaohao All Rights Reserved.
*/
public class LinearListView extends CustomLinearLayout {
private static final int[] R_styleable_LinearListView = new int[] {
android.R.attr.entries,
zhuxiaohao.com.cn.slidingtabforlistview.R.attr.dividerThickness
};
private static final int LinearListView_entries = 0;
private static final int LinearListView_dividerThickness = 1;
private View mEmptyView;
private ListAdapter mAdapter;
private boolean mAreAllItemsSelectable;
private OnItemClickListener mOnItemClickListener;
private DataSetObserver mDataObserver = new DataSetObserver() {
@Override
public void onChanged() {
setupChildren();
}
@Override
public void onInvalidated() {
setupChildren();
}
};
public LinearListView(Context context) {
this(context, null);
}
public LinearListView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs,
R_styleable_LinearListView);
// Use the thickness specified, zero being the default
final int thickness = a.getDimensionPixelSize(
LinearListView_dividerThickness, 0);
if (thickness != 0) {
setDividerThickness(thickness);
}
CharSequence[] entries = a.getTextArray(LinearListView_entries);
if (entries != null) {
setAdapter(new android.widget.ArrayAdapter<CharSequence>(context,
android.R.layout.simple_list_item_1, entries));
}
a.recycle();
}
@Override
public void setOrientation(int orientation) {
if (orientation != getOrientation()) {
int tmp = mDividerHeight;
mDividerHeight = mDividerWidth;
mDividerWidth = tmp;
}
super.setOrientation(orientation);
}
/**
* Set the divider thickness size in pixel. That means setting the divider
* height if the layout has an HORIZONTAL orientation and setting the
* divider width otherwise.
*
* @param thickness
* The divider thickness in pixel.
*/
public void setDividerThickness(int thickness) {
if (getOrientation() == VERTICAL) {
mDividerHeight = thickness;
} else {
mDividerWidth = thickness;
}
requestLayout();
}
public ListAdapter getAdapter() {
return mAdapter;
}
/**
* Sets the data behind this LinearListView.
*
* @param adapter
* The ListAdapter which is responsible for maintaining the data
* backing this list and for producing a view to represent an
* item in that data set.
*
* @see #getAdapter()
*/
public void setAdapter(ListAdapter adapter) {
if (mAdapter != null) {
mAdapter.unregisterDataSetObserver(mDataObserver);
}
mAdapter = adapter;
if (mAdapter != null) {
mAdapter.registerDataSetObserver(mDataObserver);
mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
}
setupChildren();
}
/**
* Interface definition for a callback to be invoked when an item in this
* LinearListView has been clicked.
*/
public interface OnItemClickListener {
/**
* Callback method to be invoked when an item in this LinearListView has
* been clicked.
* <p>
* Implementers can call getItemAtPosition(position) if they need to
* access the data associated with the selected item.
*
* @param parent
* The LinearListView where the click happened.
* @param view
* The view within the LinearListView that was clicked (this
* will be a view provided by the adapter)
* @param position
* The position of the view in the adapter.
* @param id
* The row id of the item that was clicked.
*/
void onItemClick(LinearListView parent, View view, int position, long id);
}
/**
* Register a callback to be invoked when an item in this LinearListView has
* been clicked.
*
* @param listener
* The callback that will be invoked.
*/
public void setOnItemClickListener(OnItemClickListener listener) {
mOnItemClickListener = listener;
}
/**
* @return The callback to be invoked with an item in this LinearListView has
* been clicked, or null id no callback has been set.
*/
public final OnItemClickListener getOnItemClickListener() {
return mOnItemClickListener;
}
/**
* Call the OnItemClickListener, if it is defined.
*
* @param view
* The view within the LinearListView that was clicked.
* @param position
* The position of the view in the adapter.
* @param id
* The row id of the item that was clicked.
* @return True if there was an assigned OnItemClickListener that was
* called, false otherwise is returned.
*/
public boolean performItemClick(View view, int position, long id) {
if (mOnItemClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
mOnItemClickListener.onItemClick(this, view, position, id);
return true;
}
return false;
}
/**
* Sets the view to show if the adapter is empty
*/
public void setEmptyView(View emptyView) {
mEmptyView = emptyView;
final ListAdapter adapter = getAdapter();
final boolean empty = ((adapter == null) || adapter.isEmpty());
updateEmptyStatus(empty);
}
/**
* When the current adapter is empty, the LinearListView can display a special
* view call the empty view. The empty view is used to provide feedback to
* the user that no data is available in this LinearListView.
*
* @return The view to show if the adapter is empty.
*/
public View getEmptyView() {
return mEmptyView;
}
/**
* Update the status of the list based on the empty parameter. If empty is
* true and we have an empty view, display it. In all the other cases, make
* sure that the layout is VISIBLE and that the empty view is GONE (if
* it's not null).
*/
private void updateEmptyStatus(boolean empty) {
if (empty) {
if (mEmptyView != null) {
mEmptyView.setVisibility(View.VISIBLE);
setVisibility(View.GONE);
} else {
// If the caller just removed our empty view, make sure the list
// view is visible
setVisibility(View.VISIBLE);
}
} else {
if (mEmptyView != null)
mEmptyView.setVisibility(View.GONE);
setVisibility(View.VISIBLE);
}
}
private void setupChildren() {
removeAllViews();
updateEmptyStatus((mAdapter == null) || mAdapter.isEmpty());
if (mAdapter == null) {
return;
}
for (int i = 0; i < mAdapter.getCount(); i++) {
View child = mAdapter.getView(i, null, this);
if (mAreAllItemsSelectable || mAdapter.isEnabled(i)) {
child.setOnClickListener(new InternalOnClickListener(i));
}
addViewInLayout(child, -1, child.getLayoutParams(), true);
}
}
/**
* Internal OnClickListener that this view associate of each of its children
* so that they can respond to OnItemClick listener's events. Avoid setting
* an OnClickListener manually. If you need it you can wrap the child in a
* simple {@link FrameLayout}.
*/
private class InternalOnClickListener implements OnClickListener {
int mPosition;
public InternalOnClickListener(int position) {
mPosition = position;
}
@Override
public void onClick(View v) {
if ((mOnItemClickListener != null) && (mAdapter != null)) {
mOnItemClickListener.onItemClick(LinearListView.this, v,
mPosition, mAdapter.getItemId(mPosition));
}
}
}
}
实现 linearlayout,让他可以像 listview 一样 setadapter,适配器要注意下要这样写.
minflater.inflate(R.layout.list_item, parent, false);
如果你这样写
minflater.inflate(R.layout.list_item, false);
会报错.
好了,不细说了,具体看代码,有注释.不是我半夜起来写代码,而是我还在加班,抽点时间把博客补齐一下.有时间我会代码继续更新,github 会继续更新该 ui代码.