Ui 篇:ScrollViewFroLinearLayout

先看效果图.![下拉刷新]!
下拉刷新
隐藏 Tab
linearlayout 列表

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代码.

点我下载

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值