android 仿QQ音乐歌单效果
来源:互联网 发布:云计算简单解释 编辑:程序博客网 时间:2024/04/29 13:59
最新的项目里面,有一个需求比较好玩,就是要仿造下QQ音乐里面的歌单上下切换效果,如下
先做一个类似的效果,测试效果如下:
而为了快速开发,不花时间在制造轮子上面,我选用是的zhy大神的一个自定义layout,代码链接为 https://github.com/hongyangAndroid/Android-StickyNavLayout
当然,直接运用并不符合我们的实际开发需求效果,所以要在原来代码上加以修改。
直接上修改后的代码:
public class StickyNavLayout extends LinearLayout {private View mTop;private View mNav;private ViewPager mViewPager;private int mTopViewHeight;private ViewGroup mInnerScrollView;private boolean isTopHidden = false;private OverScroller mScroller;private VelocityTracker mVelocityTracker;private int mTouchSlop;private int mMaximumVelocity, mMinimumVelocity;private float mLastY;private boolean mDragging;private boolean isInControl = false;public StickyNavLayout(Context context, AttributeSet attrs) {super(context, attrs);setOrientation(LinearLayout.VERTICAL);mScroller = new OverScroller(context);mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();mMaximumVelocity = ViewConfiguration.get(context).getScaledMaximumFlingVelocity();mMinimumVelocity = ViewConfiguration.get(context).getScaledMinimumFlingVelocity();}@Overrideprotected void onFinishInflate() {super.onFinishInflate();mTop = findViewById(R.id.id_stickynavlayout_topview);mNav = findViewById(R.id.id_stickynavlayout_indicator);View view = findViewById(R.id.id_stickynavlayout_viewpager);if (!(view instanceof ViewPager)) {throw new RuntimeException("id_stickynavlayout_viewpager show used by ViewPager !");}mViewPager = (ViewPager) view;}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);ViewGroup.LayoutParams params = mViewPager.getLayoutParams();params.height = getMeasuredHeight() - mNav.getMeasuredHeight() - 140;//减相同高度防止listview高度超出显示范围}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);mTopViewHeight = mTop.getMeasuredHeight() - 140;// 这里修改高度,为状态栏的高度// 具体可以通过分辨率来获取值丢进去,暂时为140}@Overridepublic 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;getCurrentScrollView();if (mInnerScrollView instanceof ScrollView) {if (mInnerScrollView.getScrollY() == 0 && isTopHidden && dy > 0&& !isInControl) {isInControl = true;ev.setAction(MotionEvent.ACTION_CANCEL);MotionEvent ev2 = MotionEvent.obtain(ev);dispatchTouchEvent(ev);ev2.setAction(MotionEvent.ACTION_DOWN);return dispatchTouchEvent(ev2);}} else if (mInnerScrollView instanceof ListView) {ListView lv = (ListView) mInnerScrollView;View c = lv.getChildAt(0);if (!isInControl && c != null && c.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);}} else if (mInnerScrollView instanceof RecyclerView) {RecyclerView rv = (RecyclerView) mInnerScrollView;if (!isInControl&& android.support.v4.view.ViewCompat.canScrollVertically(rv, -1) && 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);}@Overridepublic 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;getCurrentScrollView();if (Math.abs(dy) > mTouchSlop) {mDragging = true;if (mInnerScrollView instanceof ScrollView) {if (!isTopHidden|| (mInnerScrollView.getScrollY() == 0&& isTopHidden && dy > 0)) {initVelocityTrackerIfNotExists();mVelocityTracker.addMovement(ev);mLastY = y;return true;}} else if (mInnerScrollView instanceof ListView) {ListView lv = (ListView) mInnerScrollView;int position=lv.getFirstVisiblePosition();View c = lv.getChildAt(position);if (!isTopHidden || //(c != null //&& c.getTop() == 0//&& isTopHidden && dy > 0&&position==0)) {initVelocityTrackerIfNotExists();mVelocityTracker.addMovement(ev);mLastY = y;if(!isTopHidden&&position!=0){return false;}return true;}} else if (mInnerScrollView instanceof RecyclerView) {RecyclerView rv = (RecyclerView) mInnerScrollView;if (!isTopHidden|| (!android.support.v4.view.ViewCompat.canScrollVertically(rv, -1) && isTopHidden && dy > 0)) {initVelocityTrackerIfNotExists();mVelocityTracker.addMovement(ev);mLastY = y;return true;}}}break;case MotionEvent.ACTION_CANCEL:case MotionEvent.ACTION_UP:mDragging = false;recycleVelocityTracker();break;}return super.onInterceptTouchEvent(ev);}private void getCurrentScrollView() {int currentItem = mViewPager.getCurrentItem();PagerAdapter a = mViewPager.getAdapter();if (a instanceof FragmentPagerAdapter) {FragmentPagerAdapter fadapter = (FragmentPagerAdapter) a;Fragment item = (Fragment) fadapter.instantiateItem(mViewPager,currentItem);mInnerScrollView = (ViewGroup) (item.getView().findViewById(R.id.id_stickynavlayout_innerscrollview));} else if (a instanceof FragmentStatePagerAdapter) {FragmentStatePagerAdapter fsAdapter = (FragmentStatePagerAdapter) a;Fragment item = (Fragment) fsAdapter.instantiateItem(mViewPager,currentItem);mInnerScrollView = (ViewGroup) (item.getView().findViewById(R.id.id_stickynavlayout_innerscrollview));}}@Overridepublic boolean onTouchEvent(MotionEvent event) {initVelocityTrackerIfNotExists();mVelocityTracker.addMovement(event);int action = event.getAction();float y = event.getY();switch (action) {case MotionEvent.ACTION_DOWN:if (!mScroller.isFinished())mScroller.abortAnimation();mLastY = y;return true;case MotionEvent.ACTION_MOVE:float dy = y - mLastY;// Log.e("TAG", "dy = " + dy + " , y = " + y + " , mLastY = " +// mLastY);if (!mDragging && Math.abs(dy) > mTouchSlop) {mDragging = true;}if (mDragging) {scrollBy(0, (int) -dy);if (getScrollY() == mTopViewHeight && dy < 0) {event.setAction(MotionEvent.ACTION_DOWN);dispatchTouchEvent(event);isInControl = false;}}mLastY = y;break;case MotionEvent.ACTION_CANCEL:mDragging = false;recycleVelocityTracker();if (!mScroller.isFinished()) {mScroller.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);}public void fling(int velocityY) {mScroller.fling(0, getScrollY(), 0, velocityY, 0, 0, 0, mTopViewHeight);invalidate();}@Overridepublic void scrollTo(int x, int y) {if (y < 0) {y = 0;}if (y > mTopViewHeight) {y = mTopViewHeight;}if (y != getScrollY()) {super.scrollTo(x, y);}isTopHidden = getScrollY() == mTopViewHeight;}@Overridepublic void computeScroll() {if (mScroller.computeScrollOffset()) {scrollTo(0, mScroller.getCurrY());invalidate();}}private void initVelocityTrackerIfNotExists() {if (mVelocityTracker == null) {mVelocityTracker = VelocityTracker.obtain();}}private void recycleVelocityTracker() {if (mVelocityTracker != null) {mVelocityTracker.recycle();mVelocityTracker = null;}}}布局文件为:
<RelativeLayout xmlns:tools="http://schemas.android.com/tools" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <TextView android:layout_width="match_parent" android:layout_height="240dp" android:gravity="top|center_horizontal" android:text="模拟任务头像" android:textSize="30sp" android:textStyle="bold" /> <com.zhy.view.StickyNavLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <RelativeLayout android:id="@id/id_stickynavlayout_topview" android:layout_width="match_parent" android:layout_height="240dp" android:background="@android:color/transparent" > </RelativeLayout> <com.zhy.view.SimpleViewPagerIndicator android:id="@id/id_stickynavlayout_indicator" android:layout_width="match_parent" android:layout_height="50dp" android:background="#ffffffff" > </com.zhy.view.SimpleViewPagerIndicator> <android.support.v4.view.ViewPager android:id="@id/id_stickynavlayout_viewpager" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#44ff0000" > </android.support.v4.view.ViewPager> </com.zhy.view.StickyNavLayout></RelativeLayout>
这里解释几点:
1.源代码上的效果只要是通过dispatchTouchEvent,,onInterceptTouchEvent,onTouchEvent来控制整个布局的触碰事件分发,其次是用过scrollby(distance)来移动布局从而达到
动画效果
2.之所以在onmeasure和OnsizeChanged方法里面减去高度是因为android版QQ歌单上始终会有歌手的头像在,因此不能简单的使用appbarLayout和CoordinatorLayout来实现,因其折叠时图片是处于隐藏状态且仅存状态栏的。
3.源代码不修改的话,会有一个bug,就是在viewpager中,数据高度不一致或者在其他listview滑至下方,然后左右切换,再回到原来的listview时,这是可以看到可见项不为0时,向下滑动会带动整个viewpager,而不是listview滑至第0项,因此要在onInterceptTouchEvent中添加这样一段代码:
if(!isTopHidden&&position!=0){return false;}意思是如果当前没滑到最上,也就是viewpager和tablayout没到toolbar位置时,如果当前listview没到第0项,这时向下滑将不拦截该触碰事件,将其交给listview自己处理。
当然,在开发的时候可能部分机子会出现卡顿的情况,那么就需要改善了,需要改善的地方在哪里呢? 简单的说下能改善之处:
- 重新进行界面设计,剔除多Tab布局,优化交互
- 对布局代码进行优化,去除不必要的界面元素和ViewGroup
- 考虑是否放弃Fragment常驻内存的方案,不使用hide()和show()对Fragment进行控制,改用replace()等方案
谢谢。
0 1
- android 仿QQ音乐歌单效果
- Android仿qq健康效果
- android 仿QQ音乐建议反馈上方的提示动画效果,渐隐
- [CloudReader]Android - 仿网易云音乐歌单详情页
- 仿QQ音乐常驻底部栏播放按钮效果
- 仿QQ音乐代码
- 仿QQ音乐动画
- android仿QQ列表的效果实现
- Android 高仿QQ 界面滑动效果
- android仿qq分组列表效果
- Android 高仿QQ 界面滑动效果
- Android 高仿QQ 界面滑动效果
- 【Android】仿QQ中的点击效果
- android仿QQ下拉回弹效果
- Android实现仿qq侧边栏效果
- Android实现仿qq侧边栏效果
- Android仿QQ侧滑效果
- android仿QQ下拉回弹效果
- JS自动加载--自用 更新中
- [bzoj2707][SDOI2012]走迷宫
- Android中获取应用程序(包)的信息-----PackageManager的使用(二)
- 短工邦技术部博客开通拉
- 下载国外apk
- android 仿QQ音乐歌单效果
- QJSon的编译
- 深入分析JavaWeb 14 -- jsp九大内置对象
- mac系统如何截图
- wears Holes can drip by water
- 优化Angular应用的性能
- 安卓开发需要用什么工具
- NSString中的搜索方法
- SmartRF Packet Sniffer 使用