实现蘑菇街首页效果

来源:互联网 发布:淘宝客qq群拉人好难 编辑:程序博客网 时间:2024/04/28 07:17

打算出一个系列,专治现在市面上各种app的各种滑动不服系列,解决各种滑动冲突问题,现在已经发现了9种样式,打算一个一个一一破解,这是第一篇。

今天给大家带来的是高仿蘑菇街的首页,现在这种页面的格式很流行,一般都用在首页上,能够很好的利用手机屏幕的空间,毕竟手机屏幕就这么一点点大,想要放很多东西呢,这种布局方式还是很不错的。

先看一下效果:点击打开链接

说一下思路:其实思路很简单,把所有控件都包括进一个自定义ViewGroup里,可以继承自ScrollView,也可以继承自LinearLayout,这里我选择LinearLayout,布局上减少了一层层级,性能上显得更加优秀。然后既然自定了ViewGroup,我们就需要对滑动进行事件分发,这里我们只要对y方向的滑动进行判断就可以了,横向的并不冲突。怎么dispatch呢?看一下下面的图示:当滑动距离达到view1+view2的高度之前,自定义viewGroup(在这里我取名为MoguLayout,不知道应该取什么名字比较好)对滑动事件进行拦截,滑动事件自己处理,当view1+view2都隐藏了以后,且用户是继续向上滑动的时候MoguLayout放过,不拦截,滑动事件留给ListVIew自己处理,当view1+view2隐藏,且listview的第一个item到顶部且用户向下滑动到时候,这个时候MoguLayout又要拦截了。


ok,思路分析得也差不多了,直接上代码:

第一步,就是总体框架的搭建,主要就是Fragment和ViewPager,这里先贴出Fragment的代码;

public class ListFragment extends Fragment{    private static final int LIST_NUM = 20;    private View view;    @ViewInject(R.id.id_listview)    private ListView listView;    private int type;    private List<String> stringList = new ArrayList<>();    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        Bundle bundle = getArguments();        if (bundle != null){            type = bundle.getInt("type");        }    }    @Nullable    @Override    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {        view = inflater.inflate(R.layout.layout_fragment,null);        ViewUtils.inject(this,view);        ViewGroup parent = (ViewGroup) view.getParent();        if (parent != null){            parent.removeView(view);        }        initData();        initListView();        return view;    }    private void initData() {        stringList.clear();        for (int i = 0;i < LIST_NUM;i++){            stringList.add("列表"+type+":"+i);        }    }    private void initListView() {        listView.setAdapter(new ArrayAdapter<String>(getActivity(),android.R.layout.simple_list_item_1,stringList));    }}

第二步,就是先把界面都弄出来,activity_main和MainActivity代码如下:

<com.jiangjieqiang.mogulayout.view.MoguLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical">    <RelativeLayout        android:id="@id/id_top_banner"        android:layout_width="match_parent"        android:layout_height="200dp"        android:background="@color/lightpink">        <TextView            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:textSize="20sp"            android:text="轮播图"            android:layout_centerInParent="true"/>    </RelativeLayout>    <!--<RelativeLayout-->        <!--android:id="@id/id_horizontalview"-->        <!--android:layout_width="match_parent"-->        <!--android:layout_height="100dp"-->        <!--android:background="@color/blue">-->    <!--</RelativeLayout>-->    <HorizontalScrollView        android:id="@id/id_horizontalview"        android:layout_width="match_parent"        android:layout_height="100dp"        android:scrollbars="none">        <LinearLayout            android:id="@+id/id_horizontalview_layout"            android:layout_width="match_parent"            android:layout_height="match_parent"            android:orientation="horizontal">        </LinearLayout>    </HorizontalScrollView>    <com.jiangjieqiang.mogulayout.view.AutoHorizontalScrollView        android:id="@id/id_horizontalmenu"        android:layout_width="match_parent"        android:layout_height="50dp"        android:background="@color/white"        android:layout_gravity="center"        android:scrollbars="none">        <LinearLayout            android:id="@+id/tab_layout"            android:orientation="horizontal"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:gravity="center"></LinearLayout>    </com.jiangjieqiang.mogulayout.view.AutoHorizontalScrollView>    <android.support.v4.view.ViewPager        android:background="@color/green"        android:id="@id/id_viewpager"        android:layout_width="match_parent"        android:layout_height="wrap_content"/></com.jiangjieqiang.mogulayout.view.MoguLayout>

MainActivity:

public class MainActivity extends AppCompatActivity {    private static final int NUM_FRAGMENT = 10;    @ViewInject(R.id.id_viewpager)    private ViewPager viewPager;    @ViewInject(R.id.id_horizontalmenu)    private AutoHorizontalScrollView menu;    @ViewInject(R.id.tab_layout)    private LinearLayout tabLayouts;    @ViewInject(R.id.id_horizontalview_layout)    private LinearLayout typeLayouts;    private List<ListFragment> fragmentList = new ArrayList<>();    private List<String> titles = new ArrayList<>();    private List<TextView> textViews = new ArrayList<>();    private List<ItemVO> itemList = new ArrayList<>();    private String[] typeTitles = {"李易峰专区","当剩女遇见桃花","春季遮肉必看",            "甜心开胃菜","租男友","开学衣橱大改造","没有PS你可以吗","藏肉显瘦搭配"};    private int[] typeImgs = {R.mipmap.icon1,R.mipmap.icon1,R.mipmap.icon1,R.mipmap.icon1,            R.mipmap.icon1,R.mipmap.icon1,R.mipmap.icon1,R.mipmap.icon1};    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        ViewUtils.inject(this);        initFragments();        initView();        initTypeLayout();    }    private void initFragments() {        for (int i = 0;i < NUM_FRAGMENT;i++){            titles.add("title"+i);            ListFragment fragment = new ListFragment();            Bundle bundle = new Bundle();            bundle.putInt("type",i);            fragment.setArguments(bundle);            fragmentList.add(fragment);            LinearLayout tabLayout = (LinearLayout) LayoutInflater.from(this).inflate(R.layout.menu_item,null);            final TextView textView = (TextView)tabLayout.findViewById(R.id.tab_tv);            textView.setText(titles.get(i));            final int id = i;            tabLayout.setOnClickListener(new View.OnClickListener() {                @Override                public void onClick(View view) {                    setSelector(id);                }            });            tabLayouts.addView(tabLayout);            textViews.add(textView);        }    }    private void initView() {        setSelector(0);        viewPager.setCurrentItem(0);        viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {            @Override            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {            }            @Override            public void onPageSelected(int position) {                setSelector(position);            }            @Override            public void onPageScrollStateChanged(int state) {            }        });        viewPager.setAdapter(new FragmentPagerAdapter(getSupportFragmentManager()) {            @Override            public int getCount() {                return NUM_FRAGMENT;            }            @Override            public Fragment getItem(int position) {                return fragmentList.get(position);            }        });    }    /**     * 选中效果     * @param position     */    private void setSelector(final int position) {        for (int i = 0;i < NUM_FRAGMENT; i++){            if (position == i){                viewPager.setCurrentItem(position);                menu.resetScrollWidth(position);                textViews.get(i).setBackgroundResource(R.mipmap.bg_nav_contacts);            }else {                textViews.get(i).setBackgroundResource(R.color.alpha);            }        }    }    /**     * 初始化横向滑动的layouts     */    private void initTypeLayout() {        initItemList();        for (final ItemVO itemVO : itemList){            FrameLayout tabLayout = (FrameLayout)LayoutInflater.from(this).inflate(R.layout.horizontal_item,null);            ImageView imageView = (ImageView)tabLayout.findViewById(R.id.id_horizontal_item_img);            TextView textView = (TextView)tabLayout.findViewById(R.id.id_horizontal_item_desc);            imageView.setImageResource(itemVO.getImage());            imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);            textView.setText(itemVO.getDesc());            tabLayout.setOnClickListener(new View.OnClickListener() {                @Override                public void onClick(View view) {                    //intent进入其他页面                    //……                    Toast.makeText(MainActivity.this, "进入页面", Toast.LENGTH_SHORT).show();                }            });            LinearLayout.LayoutParams vlp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,                    ViewGroup.LayoutParams.MATCH_PARENT);            vlp.setMargins(calculateDpToPx(5),calculateDpToPx(5),calculateDpToPx(5),calculateDpToPx(5));            typeLayouts.addView(tabLayout,vlp);        }    }    private void initItemList() {        itemList.clear();        for (int i = 0;i < typeImgs.length;i ++){            ItemVO itemVO = new ItemVO(typeTitles[i],typeImgs[i]);            itemList.add(itemVO);        }    }    private int calculateDpToPx(int padding_in_dp){        final float scale = getResources().getDisplayMetrics().density;        return  (int) (padding_in_dp * scale + 0.5f);    }}
其中我对listview的导航进行了自定义view的处理,其实我就是在其中公开了一个方法,给MainActivity里的viewPager当滑动到页面的时候调用:

AutoHorizontalScrollView:

public class AutoHorizontalScrollView extends HorizontalScrollView{    public AutoHorizontalScrollView(Context context) {        super(context);    }    public AutoHorizontalScrollView(Context context, AttributeSet attrs) {        super(context, attrs);    }    public AutoHorizontalScrollView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }    /**     * 当item过多时,主屏幕显示不了,就重置item的width的位置,并让下一个item显示在屏幕的一半     * @param index     */    public void resetScrollWidth(int index) {        ViewGroup parent = (ViewGroup) getChildAt(0);        if (index < 0 || index >= parent.getChildCount()) {            return;        }        View view;        int left = 0;        for (int i = 0; i < index; i++) {            view = parent.getChildAt(i);            view.measure(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);            left += view.getMeasuredWidth();        }        view = parent.getChildAt(index);        view.measure(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);        int right = left + view.getMeasuredWidth();        if (right < getWidth()/ 2) {            this.smoothScrollTo(0, 0);        }        else {            this.smoothScrollTo(right - (getWidth()/ 2), 0);        }    }}

3、重头戏,也就是重点MoguLayout的实现;

首先,就是对一些我们要使用到的变量进行定义以及初始化,然后呢我们有viewpager,viewpager是不会自己去测量里面孩子的高度的,我们需要在onMeasure()方法里给它设置一个高度。

public class MoguLayout extends LinearLayout{    private View topView;    private View horizontalScrollView;    private AutoHorizontalScrollView menu;    private ViewPager viewPager;    private ListView listView;    private OverScroller scroller;    private VelocityTracker mVelocityTracker;    private int mTouchSlop;    private int mMaximumVelocity, mMinimumVelocity;    private int distanceFromViewPagerToX;    private float mLastY;    private boolean mDragging;    private boolean isInControl = false;    private boolean isTopHidden = false;    public MoguLayout(Context context, AttributeSet attrs) {        super(context, attrs);        setOrientation(LinearLayout.VERTICAL);        scroller = new OverScroller(context);        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();        mMaximumVelocity = ViewConfiguration.get(context).getScaledMaximumFlingVelocity();        mMinimumVelocity = ViewConfiguration.get(context).getScaledMinimumFlingVelocity();    }    @Override    protected void onFinishInflate() {        super.onFinishInflate();        topView = findViewById(R.id.id_top_banner);        horizontalScrollView = findViewById(R.id.id_horizontalview);        viewPager = (ViewPager)findViewById(R.id.id_viewpager);        menu = (AutoHorizontalScrollView)findViewById(R.id.id_horizontalmenu);    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        ViewGroup.LayoutParams params = viewPager.getLayoutParams();        params.height = getMeasuredHeight() - menu.getMeasuredHeight();    }

然后,我们需要随时获取view1+view2在屏幕上显示的高度,这个高度我们要用来进行对滑动事件是否拦截的判断。

@Override    protected void onSizeChanged(int w, int h, int oldw, int oldh) {        super.onSizeChanged(w, h, oldw, oldh);        distanceFromViewPagerToX = topView.getMeasuredHeight()+horizontalScrollView.getMeasuredHeight();    }

之后就是三部曲了:1、自定了viewGroup,滑动就要自己写,2、dispatchTouchEvent()进行分发,3、onInterceptTouchEvent()进行拦截判断,具体的逻辑分析之前已经分析过了,那就上代码咯:

@Override    public boolean onTouchEvent(MotionEvent event) {        initVelocityTrackerIfNotExists();        mVelocityTracker.addMovement(event);        int action = event.getAction();        float y = event.getY();        switch (action) {            case MotionEvent.ACTION_DOWN:                if (!scroller.isFinished())                    scroller.abortAnimation();                mLastY = y;                return true;            case MotionEvent.ACTION_MOVE:                float dy = y - mLastY;                //判断是滑动还是点击                if (!mDragging && Math.abs(dy) > mTouchSlop) {                    mDragging = true;                }                if (mDragging) {                    scrollBy(0, (int) -dy);                    // 如果topView隐藏,且上滑动时,则改变当前事件为ACTION_DOWN                    if (getScrollY() == distanceFromViewPagerToX && dy < 0) {                        event.setAction(MotionEvent.ACTION_DOWN);                        dispatchTouchEvent(event);                        isInControl = false;                    }                }                mLastY = y;                break;            case MotionEvent.ACTION_CANCEL:                mDragging = false;                recycleVelocityTracker();                if (!scroller.isFinished()) {                    scroller.abortAnimation();                }                break;            case MotionEvent.ACTION_UP:                mDragging = false;                //初始化                mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);                int velocityY = (int) mVelocityTracker.getYVelocity();                if (Math.abs(velocityY) > mMinimumVelocity) {                    fling(-velocityY);                }                recycleVelocityTracker();                break;        }        return super.onTouchEvent(event);    }    /**     * 当滑动速度比较大的时候,实现快速滑动     * @param velocityY     */    public void fling(int velocityY) {        //        scroller.fling(0, getScrollY(), 0, velocityY, 0, 0, 0, distanceFromViewPagerToX);        invalidate();    }    @Override    public void scrollTo(int x, int y) {        if (y < 0) {            y = 0;        }        if (y > distanceFromViewPagerToX) {            y = distanceFromViewPagerToX;        }        if (y != getScrollY()) {            super.scrollTo(x, y);        }        isTopHidden = getScrollY() == distanceFromViewPagerToX;    }    @Override    public void computeScroll() {        if (scroller.computeScrollOffset()) {            scrollTo(0, scroller.getCurrY());            invalidate();        }    }    @Override    public boolean dispatchTouchEvent(MotionEvent ev) {        int action = ev.getAction();        float y = ev.getY();        switch (action) {            case MotionEvent.ACTION_DOWN:                mLastY = y;                break;            case MotionEvent.ACTION_MOVE:                float dy = y - mLastY;                getCurrentListView();                View view = listView.getChildAt(listView.getFirstVisiblePosition());                if (!isInControl && view != null && view.getTop() == 0 && isTopHidden && dy > 0) {                    isInControl = true;                    ev.setAction(MotionEvent.ACTION_CANCEL);                    MotionEvent ev2 = MotionEvent.obtain(ev);                    dispatchTouchEvent(ev);                    ev2.setAction(MotionEvent.ACTION_DOWN);                    return dispatchTouchEvent(ev2);                }                break;        }        return super.dispatchTouchEvent(ev);    }    private void getCurrentListView() {        int currentItem = viewPager.getCurrentItem();        PagerAdapter a = viewPager.getAdapter();        FragmentPagerAdapter fadapter = (FragmentPagerAdapter) a;        Fragment item = (Fragment) fadapter.instantiateItem(viewPager,                currentItem);        listView = (ListView) (item.getView().findViewById(R.id.id_listview));    }    @Override    public boolean onInterceptTouchEvent(MotionEvent ev) {        final int action = ev.getAction();        float y = ev.getY();        switch (action) {            case MotionEvent.ACTION_DOWN:                mLastY = y;                break;            case MotionEvent.ACTION_MOVE:                float dy = y - mLastY;                getCurrentListView();                if (Math.abs(dy) > mTouchSlop) {                    //滑动                    mDragging = true;                    View view = listView.getChildAt(listView.getFirstVisiblePosition());                    // 拦截条件:topView没有隐藏                    // 或listView在顶部 && topView隐藏 && 下拉                    if (!isTopHidden || (view != null && view.getTop() == 0 && isTopHidden && dy > 0)) {                        initVelocityTrackerIfNotExists();                        mLastY = y;                        mVelocityTracker.addMovement(ev);                        return true;                    }                }                break;            case MotionEvent.ACTION_CANCEL:            case MotionEvent.ACTION_UP:                mDragging = false;                recycleVelocityTracker();                break;        }        return super.onInterceptTouchEvent(ev);    }    private void initVelocityTrackerIfNotExists() {        if (mVelocityTracker == null) {            mVelocityTracker = VelocityTracker.obtain();        }    }    private void recycleVelocityTracker() {        if (mVelocityTracker != null) {            mVelocityTracker.recycle();            mVelocityTracker = null;        }    }

这里我们对快速滑动进行了特殊处理,当滑动速度大于minVelocityTracker的时候,我们处理的圆润一点,直接滑动到view1+view显示或者隐藏两种状态。ok,基本就大功告成了!

项目地址:点击打开链接


1 0
原创粉丝点击