写一个Anroid通用style的详情页基类

开发中经常需要写一个带tab切换并且tab有吸顶效果而且头要渐变效果的详情页,如下图:
![


写一个Anroid通用style的详情页基类_第1张图片
TT8(K5X~CK$NH(Z@VWC%0PU.png

之前做项目时,一个人需要维护三四种这样类似的页面,每次在scrollView滚动时做一些列的计算都很痛苦,各种计算吸顶以及滚动时tab选中的位置。后来就萌生了一个想法,直接写一个基类,在基类里面完成完成这一系列的计算,然后子类直接继承基类,实现几个画页面的方法就好了!下面直接上代码:

基类activity:

private View mActionBarView;
    private LinearLayout mContainerLayout;
    private ObservableScrollView mScrollView;
    private CommonDetailTabLayout mTabLayout1;
    private CommonDetailTabLayout mTabLayout2;
    protected ViewGroup mBottomContainer;

    private int mStatusBarHeight = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.lesson_detail_layout);
        initView();
    }

    private void initView() {
        int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
        mStatusBarHeight = getResources().getDimensionPixelSize(resourceId);
        initTabLayout();
        mActionBarView = getView(R.id.ll_actionbar);
        mActionBarView.getBackground().setAlpha(0);
        mScrollView = getView(R.id.sv_content);
        mScrollView.addScrollViewCallbacks(mCallBacks);
        mContainerLayout = getView(R.id.ll_container);
        if (getHeaderView() != null) {
            mContainerLayout.addView(getHeaderView());
        }
        mContainerLayout.addView(mTabLayout2);
        for (View tabView : getTabViews()) {
            if (tabView != null) {
                mContainerLayout.addView(tabView);
            }
        }
        mBottomContainer = getView(R.id.ll_bottom_container);
        if (getBottomView() == null) {
            mBottomContainer.setVisibility(View.GONE);
        } else {
            mBottomContainer.setVisibility(View.VISIBLE);
        }

    }

    protected abstract View getHeaderImageView();

    protected abstract View getHeaderView();

    protected abstract View getBottomView();

    protected abstract ArrayList getTabViews();

    protected void setTabData(String... data) {
        ArrayList tabData = new ArrayList<>();
        int length = data.length;
        if (length != getTabViews().size()) {
            return;

        }
        for (int i = 0; i < length; i++) {
            CommonDetailTabLayout.TabObject tabObject = new CommonDetailTabLayout.TabObject();
            tabObject.tabName = data[i];
            final int finalI = i;
            tabObject.listener = new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    int height = getHeaderView().getMeasuredHeight()  - DimenUtils.dip2px(mActivity,52);
                    for (int j = 0; j < finalI ; j ++){
                        height += getTabViews().get(j).getMeasuredHeight();
                    }
                    mTabLayout1.check(finalI);
                    mTabLayout2.check(finalI);
                    mScrollView.scrollVerticallyTo(height);
                }
            };
            tabData.add(tabObject);
        }
        mTabLayout1.setData(tabData);
        mTabLayout2.setData(tabData);
    }

    private void initTabLayout() {
        mTabLayout1 = getView(R.id.tab);
        mTabLayout1.setVisibility(View.GONE);
        mTabLayout2 = new CommonDetailTabLayout(mActivity);
    }

    private ObservableScrollViewCallbacks mCallBacks = new ObservableScrollViewCallbacks() {
        @Override
        public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {
            //计算actionbar透明度
            float progress = (scrollY * 1.0f)
                    / (getHeaderImageView().getHeight() - DimenUtils.dip2px(mActivity,52));
            progress = progress > 1 ? 1 : progress;
            mActionBarView.getBackground().setAlpha((int) (255 * progress));

            //滚动选中tab
            int size = getTabViews().size();
            for (int i = 0; i < size; i++) {
                View view = getTabViews().get(i);
                int top = view.getTop() - DimenUtils.dip2px(mActivity, 94);
                int bottom = top + view.getMeasuredHeight();
                if (scrollY > top && scrollY < bottom) {
                    mTabLayout1.check(i);
                    mTabLayout2.check(i);
                    break;
                }
                if (i == size - 2 && scrollY > top) {
                    mTabLayout1.check(size - 1);
                    mTabLayout2.check(size - 1);
                }
            }
            //控制顶部tab的显示与隐藏
            int headerHeight = DimenUtils.dip2px(mActivity, 52) + mStatusBarHeight;
            int[] loc = new int[2];
            mTabLayout2.getLocationOnScreen(loc);
            mTabLayout1.setVisibility(loc[1] >= headerHeight ? View.GONE : View.VISIBLE);

        }

        @Override
        public void onDownMotionEvent() {

        }

        @Override
        public void onUpOrCancelMotionEvent(ScrollState scrollState) {

        }
    };

tab控件

public class CommonDetailTabLayout extends RadioGroup {
    private Context mContext;
    public ArrayList mList = new ArrayList();

    public CommonDetailTabLayout(Context context) {
        super(context);
        mContext = context;
        initConfig();
    }

    public CommonDetailTabLayout(Context context, AttributeSet attributeSet) {
        super(context, attributeSet);
        mContext = context;
        initConfig();
    }

    private void initConfig() {
        setOrientation(HORIZONTAL);
        setLayoutParams(new AbsListView.LayoutParams(AbsListView.LayoutParams.MATCH_PARENT, Utils.dip2px(mContext, 42)));
        int padding = Utils.dip2px(mContext,10);
        setBackgroundResource(R.drawable.bg_common_twoline);
        setPadding(padding, 0, padding, 0);
    }

    public void setData(ArrayList list) {
        if (list == null) {
            return;
        }
        removeAllViews();
        mList.clear();
        int size = list.size();
        for (int i = 0; i < size; i++) {
            RadioButton radio = (RadioButton) LayoutInflater.from(mContext).inflate(R.layout.detai_radio_button, this, false);
            radio.setId(i);
            if (i == 0) {
                radio.setChecked(true);
            }
            radio.setText(list.get(i).tabName);
            radio.setOnClickListener(list.get(i).listener);
            mList.add(radio);
            addView(radio);
        }
    }

    public static class TabObject {
        public String tabName;
        public OnClickListener listener;
    }

    public ArrayList getRadioList() {
        return mList;
    }

    public void resetChecked() {
        check(getChildAt(0).getId());
    }

}

重写的scrollview

public class ObservableScrollView extends ScrollView implements ObserveScrollable {

    // Fields that should be saved onSaveInstanceState
    private int mPrevScrollY;
    private int mScrollY;

    // Fields that don't need to be saved onSaveInstanceState
    private ObservableScrollViewCallbacks mCallbacks;
    private List mCallbackCollection;
    private ScrollState mScrollState;
    private boolean mFirstScroll;
    private boolean mDragging;
    private boolean mIntercepted;
    private MotionEvent mPrevMoveEvent;
    private ViewGroup mTouchInterceptionViewGroup;

    public ObservableScrollView(Context context) {
        super(context);
    }

    public ObservableScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public ObservableScrollView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public void onRestoreInstanceState(Parcelable state) {
        SavedState ss = (SavedState) state;
        mPrevScrollY = ss.prevScrollY;
        mScrollY = ss.scrollY;
        super.onRestoreInstanceState(ss.getSuperState());
    }

    @Override
    public Parcelable onSaveInstanceState() {
        Parcelable superState = super.onSaveInstanceState();
        SavedState ss = new SavedState(superState);
        ss.prevScrollY = mPrevScrollY;
        ss.scrollY = mScrollY;
        return ss;
    }

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
        if (hasNoCallbacks()) {
            return;
        }
        mScrollY = t;

        dispatchOnScrollChanged(t, mFirstScroll, mDragging);
        if (mFirstScroll) {
            mFirstScroll = false;
        }

        if (mPrevScrollY < t) {
            mScrollState = ScrollState.UP;
        } else if (t < mPrevScrollY) {
            mScrollState = ScrollState.DOWN;
            //} else {
            // Keep previous state while dragging.
            // Never makes it STOP even if scrollY not changed.
            // Before Android 4.4, onTouchEvent calls onScrollChanged directly for ACTION_MOVE,
            // which makes mScrollState always STOP when onUpOrCancelMotionEvent is called.
            // STOP state is now meaningless for ScrollView.
        }
        mPrevScrollY = t;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (hasNoCallbacks()) {
            return super.onInterceptTouchEvent(ev);
        }
        switch (ev.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                // Whether or not motion events are consumed by children,
                // flag initializations which are related to ACTION_DOWN events should be executed.
                // Because if the ACTION_DOWN is consumed by children and only ACTION_MOVEs are
                // passed to parent (this view), the flags will be invalid.
                // Also, applications might implement initialization codes to onDownMotionEvent,
                // so call it here.
                mFirstScroll = mDragging = true;
                dispatchOnDownMotionEvent();
                break;
        }
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (hasNoCallbacks()) {
            return super.onTouchEvent(ev);
        }
        switch (ev.getActionMasked()) {
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                mIntercepted = false;
                mDragging = false;
                dispatchOnUpOrCancelMotionEvent(mScrollState);
                break;
            case MotionEvent.ACTION_MOVE:
                if (mPrevMoveEvent == null) {
                    mPrevMoveEvent = ev;
                }
                float diffY = ev.getY() - mPrevMoveEvent.getY();
                mPrevMoveEvent = MotionEvent.obtainNoHistory(ev);
                if (getCurrentScrollY() - diffY <= 0) {
                    // Can't scroll anymore.

                    if (mIntercepted) {
                        // Already dispatched ACTION_DOWN event to parents, so stop here.
                        return false;
                    }

                    // Apps can set the interception target other than the direct parent.
                    final ViewGroup parent;
                    if (mTouchInterceptionViewGroup == null) {
                        parent = (ViewGroup) getParent();
                    } else {
                        parent = mTouchInterceptionViewGroup;
                    }

                    // Get offset to parents. If the parent is not the direct parent,
                    // we should aggregate offsets from all of the parents.
                    float offsetX = 0;
                    float offsetY = 0;
                    for (View v = this; v != null && v != parent; v = (View) v.getParent()) {
                        offsetX += v.getLeft() - v.getScrollX();
                        offsetY += v.getTop() - v.getScrollY();
                    }
                    final MotionEvent event = MotionEvent.obtainNoHistory(ev);
                    event.offsetLocation(offsetX, offsetY);

                    if (parent.onInterceptTouchEvent(event)) {
                        mIntercepted = true;

                        // If the parent wants to intercept ACTION_MOVE events,
                        // we pass ACTION_DOWN event to the parent
                        // as if these touch events just have began now.
                        event.setAction(MotionEvent.ACTION_DOWN);

                        // Return this onTouchEvent() first and set ACTION_DOWN event for parent
                        // to the queue, to keep events sequence.
                        post(new Runnable() {
                            @Override
                            public void run() {
                                parent.dispatchTouchEvent(event);
                            }
                        });
                        return false;
                    }
                    // Even when this can't be scrolled anymore,
                    // simply returning false here may cause subView's click,
                    // so delegate it to super.
                    return super.onTouchEvent(ev);
                }
                break;
        }
        return super.onTouchEvent(ev);
    }

    @Override
    public void setScrollViewCallbacks(ObservableScrollViewCallbacks listener) {
        mCallbacks = listener;
    }

    @Override
    public void addScrollViewCallbacks(ObservableScrollViewCallbacks listener) {
        if (mCallbackCollection == null) {
            mCallbackCollection = new ArrayList();
        }
        mCallbackCollection.add(listener);
    }

    @Override
    public void removeScrollViewCallbacks(ObservableScrollViewCallbacks listener) {
        if (mCallbackCollection != null) {
            mCallbackCollection.remove(listener);
        }
    }

    @Override
    public void clearScrollViewCallbacks() {
        if (mCallbackCollection != null) {
            mCallbackCollection.clear();
        }
    }

    @Override
    public void setTouchInterceptionViewGroup(ViewGroup viewGroup) {
        mTouchInterceptionViewGroup = viewGroup;
    }

    @Override
    public void scrollVerticallyTo(int y) {
        scrollTo(0, y);
    }

    @Override
    public int getCurrentScrollY() {
        return mScrollY;
    }

    private void dispatchOnDownMotionEvent() {
        if (mCallbacks != null) {
            mCallbacks.onDownMotionEvent();
        }
        if (mCallbackCollection != null) {
            for (int i = 0; i < mCallbackCollection.size(); i++) {
                ObservableScrollViewCallbacks callbacks = mCallbackCollection.get(i);
                callbacks.onDownMotionEvent();
            }
        }
    }

    private void dispatchOnScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {
        if (mCallbacks != null) {
            mCallbacks.onScrollChanged(scrollY, firstScroll, dragging);
        }
        if (mCallbackCollection != null) {
            for (int i = 0; i < mCallbackCollection.size(); i++) {
                ObservableScrollViewCallbacks callbacks = mCallbackCollection.get(i);
                callbacks.onScrollChanged(scrollY, firstScroll, dragging);
            }
        }
    }
    private void dispatchOnUpOrCancelMotionEvent(ScrollState scrollState) {
        if (mCallbacks != null) {
            mCallbacks.onUpOrCancelMotionEvent(scrollState);
        }
        if (mCallbackCollection != null) {
            for (int i = 0; i < mCallbackCollection.size(); i++) {
                ObservableScrollViewCallbacks callbacks = mCallbackCollection.get(i);
                callbacks.onUpOrCancelMotionEvent(scrollState);
            }
        }
    }

    private boolean hasNoCallbacks() {
        return mCallbacks == null && mCallbackCollection == null;
    }

    static class SavedState extends BaseSavedState {
        int prevScrollY;
        int scrollY;

        /**
         * Called by onSaveInstanceState.
         */
        SavedState(Parcelable superState) {
            super(superState);
        }

        /**
         * Called by CREATOR.
         */
        private SavedState(Parcel in) {
            super(in);
            prevScrollY = in.readInt();
            scrollY = in.readInt();
        }

        @Override
        public void writeToParcel(Parcel out, int flags) {
            super.writeToParcel(out, flags);
            out.writeInt(prevScrollY);
            out.writeInt(scrollY);
        }

        public static final Creator CREATOR
                = new Creator() {
            @Override
            public SavedState createFromParcel(Parcel in) {
                return new SavedState(in);
            }

            @Override
            public SavedState[] newArray(int size) {
                return new SavedState[size];
            }
        };
    }

}

基类activity的layout




    
        
        
    

    

    

    


下面是我自己继承基类写的一个小demo

public class DepositDetailActivity extends DetailBaseActivity {
    private DetailHeaderWidget mHeaderWidget;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        mHeaderWidget = new DetailHeaderWidget(this);
        super.onCreate(savedInstanceState);
        setTitle("课程详情");
        setTabData("1111","2222","3333");
    }

    @Override
    protected View getHeaderImageView() {
        return mHeaderWidget.getTopImage();
    }

    @Override
    protected View getHeaderView() {
        return mHeaderWidget.getView();
    }

    @Override
    protected ArrayList getTabViews() {
        ArrayList list = new ArrayList<>();
        for (int i = 0 ; i < 3 ; i++){
            TextView textView = new TextView(mActivity);
            textView.setText("第" + i);
            textView.setGravity(Gravity.CENTER);
            textView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, Utils.dip2px(mActivity,i == 2 ? 800 : 800)));
            list.add(textView);
        }
        return list;
    }
}

效果如图:

![LZ2MUZ(SD}BAN[RYJ$]FX.png](http://upload-images.jianshu.io/upload_images/4266338-636ae9402a5cef05.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

![]TKL434$JD][email protected]](http://upload-images.jianshu.io/upload_images/4266338-c88133055aa16992.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
就重写这点代码就可以实现这些效果了,是不是很简单?

你可能感兴趣的:(写一个Anroid通用style的详情页基类)