实现蘑菇街首页效果
来源:互联网 发布:淘宝客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,基本就大功告成了!
项目地址:点击打开链接
- 实现蘑菇街首页效果
- Android-自定义多TAB悬浮控件实现蘑菇街首页效果
- Android-自定义多TAB悬浮控件实现蘑菇街首页效果
- Android自定义多TAB悬浮控件实现蘑菇街首页效果
- 美丽说蘑菇街首页效果(UITableView和UIScrollerView联动)
- Android 实现蘑菇街购物车动画效果
- android实现蘑菇街购物车动画效果
- Android 实现蘑菇街购物车动画效果
- Android 实现蘑菇街购物车动画效果
- Android 实现蘑菇街购物车动画效果
- Android 实现蘑菇街购物车动画效果
- Android 实现蘑菇街购物车动画效果
- 仿蘑菇街首页升级版
- android 瀑布流效果(仿蘑菇街)
- android 瀑布流效果(仿蘑菇街)
- android 瀑布流效果(仿蘑菇街)
- android 瀑布流效果(仿蘑菇街)
- android 瀑布流效果(仿蘑菇街)
- CComPtr用法
- Cordys BOP租户使用中常见疑问——租户对接口进行授权
- Eclipse导入Hadoop源码项目
- Tomact8部署在linux下启动很慢详解
- Java中使用正则表达式
- 实现蘑菇街首页效果
- Spring与Mybatis整合的MapperScannerConfigurer处理过程源码分析
- 一种展柜的设计构想
- 想获得第一手的新闻线索但无从下手?想确认最准确的行业动态但找不到来源?
- opencv系列教程
- 解决app 监听应用退出 需要数据还原问题(例如账户登陆状态的保持)
- 19. Remove Nth Node From End of List
- UICollectionView实现item的重新排布
- 一览IT名人的教育成长经历