Ui 篇:ScrollViewFroLinearLayout
先看效果图.![下拉刷新]!
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(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. * * 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代码.
点我下载
更多相关文章
- 【Android】问题记录
- Android(安卓)TextView 丰富多彩的字体样式代码
- fragment中加载高德地图出现滑动冲突解决。
- Android(安卓)项目中常用到的第三方组件
- Android获取当前时间与星期几
- android 自带的日期控件 DatePicker
- android使用opengl es2.0播放视频
- 将网页嵌入到android应用中
- android WheelView时间选择器