美文网首页
实现蘑菇街首页效果

实现蘑菇街首页效果

作者: JJQ3 | 来源:发表于2017-04-20 17:24 被阅读161次

近期在做博客迁移,原文地址:原文地址

今天给大家带来的是高仿蘑菇街的首页,现在这种页面的格式很流行,一般都用在首页上,能够很好的利用手机屏幕的空间,毕竟手机屏幕就这么一点点大,想要放很多东西呢,这种布局方式还是很不错的。
先看一下效果:点击打开链接
说一下思路:其实思路很简单,把所有控件都包括进一个自定义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,基本就大功告成了!
项目地址:点击打开链接

我的公共账号,欢迎关注

相关文章

网友评论

      本文标题:实现蘑菇街首页效果

      本文链接:https://www.haomeiwen.com/subject/bvvlzttx.html