├── .gitignore ├── .idea ├── .name ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── gradle.xml ├── inspectionProfiles │ ├── Project_Default.xml │ └── profiles_settings.xml ├── libraries │ ├── animated_vector_drawable_23_3_0.xml │ ├── appcompat_v7_23_3_0.xml │ ├── design_23_3_0.xml │ ├── recyclerview_v7_23_3_0.xml │ ├── support_annotations_23_3_0.xml │ ├── support_v4_23_3_0.xml │ └── support_vector_drawable_23_3_0.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml ├── vcs.xml └── workspace.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── cn │ │ └── ittiger │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── cn │ │ │ └── ittiger │ │ │ └── ucpage │ │ │ ├── DemoActivity.java │ │ │ └── view │ │ │ ├── ContentHeadView.java │ │ │ ├── ContentView.java │ │ │ ├── MoveView.java │ │ │ ├── PageHeadView.java │ │ │ ├── PageNavigationView.java │ │ │ ├── TouchMoveView.java │ │ │ └── UCIndexView.java │ └── res │ │ ├── layout │ │ ├── activity_uc_index_view.xml │ │ ├── content_head_view_layout.xml │ │ ├── content_view_layout.xml │ │ ├── page_head_view_layout.xml │ │ └── page_navigation_view_layout.xml │ │ ├── menu │ │ └── menu_main.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-v21 │ │ └── styles.xml │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── cn │ └── ittiger │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── local.properties └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | .DS_Store 4 | local.properties 5 | gradle.properties 6 | /.idea 7 | /build 8 | /captures 9 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | UCIndexAnimation -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 23 | 24 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /.idea/libraries/animated_vector_drawable_23_3_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/libraries/appcompat_v7_23_3_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/libraries/design_23_3_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/libraries/recyclerview_v7_23_3_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/libraries/support_annotations_23_3_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/libraries/support_v4_23_3_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /.idea/libraries/support_vector_drawable_23_3_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UC浏览器首页滑动动画实现 2 | 3 | #### 现在已采用Behavior实现此效果,代码在这里 [https://github.com/huyongli/UCMainViewForBehavior](https://github.com/huyongli/UCMainViewForBehavior) 4 | 5 | 6 | 库的相关讲解见我的博客[ittiger.cn](http://ittiger.cn/2016/05/26/UC%E6%B5%8F%E8%A7%88%E5%99%A8%E9%A6%96%E9%A1%B5%E6%BB%91%E5%8A%A8%E5%8A%A8%E7%94%BB%E5%AE%9E%E7%8E%B0/) 7 | 8 | 9 | 个人微信公众号,欢迎扫码关注交流: 10 | ![](https://img-blog.csdnimg.cn/2019052410035231.jpg) 11 | 12 | 13 | 效果图如下:
14 | ![这里写图片描述](http://img.blog.csdn.net/20160526232024470) 15 | ![这里写图片描述](http://img.blog.csdn.net/20160526232223237) 16 | ![这里写图片描述](http://img.blog.csdn.net/20160526232326059) 17 | ![这里写图片描述](http://img.blog.csdn.net/20160526232403427) 18 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.3" 6 | 7 | defaultConfig { 8 | applicationId "cn.ittiger" 9 | minSdkVersion 14 10 | targetSdkVersion 23 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | testCompile 'junit:junit:4.12' 25 | compile 'com.android.support:appcompat-v7:23.3.0' 26 | compile 'com.android.support:design:23.3.0' 27 | } 28 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /home/baina/ylhu/program/android-sdk-linux/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/cn/ittiger/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package cn.ittiger; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 10 | 11 | 16 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/java/cn/ittiger/ucpage/DemoActivity.java: -------------------------------------------------------------------------------- 1 | package cn.ittiger.ucpage; 2 | 3 | import cn.ittiger.ucpage.view.UCIndexView; 4 | 5 | import android.app.Activity; 6 | import android.os.Bundle; 7 | 8 | public class DemoActivity extends Activity { 9 | UCIndexView mUCIndexView; 10 | boolean flag = false; 11 | 12 | @Override 13 | protected void onCreate(Bundle savedInstanceState) { 14 | 15 | super.onCreate(savedInstanceState); 16 | setContentView(R.layout.activity_uc_index_view); 17 | mUCIndexView = (UCIndexView) findViewById(R.id.ucindexview); 18 | } 19 | 20 | @Override 21 | public void onBackPressed() { 22 | 23 | if(flag == false && !mUCIndexView.isPullRestoreEnable()) { 24 | mUCIndexView.onBackRestore(); 25 | flag = true; 26 | } else { 27 | super.onBackPressed(); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/src/main/java/cn/ittiger/ucpage/view/ContentHeadView.java: -------------------------------------------------------------------------------- 1 | package cn.ittiger.ucpage.view; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | 6 | /** 7 | * 内容视图的头部(对应UC首页新闻视图完全展示后的新闻类型导航头部部分,初始时隐藏) 8 | * 9 | * @author laohu 10 | * @link http://ittiger.cn 11 | */ 12 | public class ContentHeadView extends MoveView { 13 | 14 | public ContentHeadView(Context context) { 15 | super(context); 16 | } 17 | 18 | public ContentHeadView(Context context, AttributeSet attrs) { 19 | super(context, attrs); 20 | } 21 | 22 | public ContentHeadView(Context context, AttributeSet attrs, int defStyleAttr) { 23 | super(context, attrs, defStyleAttr); 24 | } 25 | 26 | public synchronized void onShowAnimation(float step) { 27 | 28 | if(isShowFinish()) { 29 | return; 30 | } 31 | updateMarginTop(-getShowMoveStep(step)); 32 | } 33 | 34 | public synchronized void onHideAnimation(float step) { 35 | 36 | if(isHideFinish()) { 37 | return; 38 | } 39 | updateMarginTop(getHideMoveStep(step)); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/src/main/java/cn/ittiger/ucpage/view/ContentView.java: -------------------------------------------------------------------------------- 1 | package cn.ittiger.ucpage.view; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | 6 | /** 7 | * @author laohu 8 | * @link http://ittiger.cn 9 | * 10 | * 内容视图(对应UC首页中展示新闻内容的视图),主要滑动操作在此视图上 11 | */ 12 | public class ContentView extends TouchMoveView { 13 | 14 | public ContentView(Context context) { 15 | super(context); 16 | } 17 | 18 | public ContentView(Context context, AttributeSet attrs) { 19 | super(context, attrs); 20 | } 21 | 22 | public ContentView(Context context, AttributeSet attrs, int defStyleAttr) { 23 | super(context, attrs, defStyleAttr); 24 | } 25 | 26 | public synchronized void onShowAnimation(float step) { 27 | 28 | if(isShowFinish()) { 29 | return; 30 | } 31 | updateMarginTop(-getShowMoveStep(step)); 32 | } 33 | 34 | public synchronized void onHideAnimation(float step) { 35 | 36 | if(isHideFinish()) { 37 | return; 38 | } 39 | updateMarginTop(getHideMoveStep(step)); 40 | } 41 | 42 | /** 43 | * 获取当前视图在展示过程中已经滑离初始化位置的距离 44 | * @return 45 | */ 46 | public int getShowOffset() { 47 | 48 | return mHideStopMarginTop - getMarginTop(); 49 | } 50 | 51 | /** 52 | * 获取当前视图在恢复过程中已经滑离展示停止位置的距离 53 | * @return 54 | */ 55 | public int getHideOffset() { 56 | 57 | return getMarginTop() - mShowStopMarginTop; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /app/src/main/java/cn/ittiger/ucpage/view/MoveView.java: -------------------------------------------------------------------------------- 1 | package cn.ittiger.ucpage.view; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.widget.FrameLayout; 6 | 7 | /** 8 | * @author laohu 9 | * @link http://ittiger.cn 10 | * 11 | */ 12 | public class MoveView extends FrameLayout { 13 | 14 | /** 15 | * 需要滑动的高度 16 | */ 17 | protected int mNeedMoveHeight = -1; 18 | /** 19 | * Show展示时的停止值,此处我设置向上滑动为Show的状态,也可称为展示状态 20 | */ 21 | protected int mShowStopMarginTop; 22 | /** 23 | * Hide时的停止值,此处我设置向下滑动为Hide状态,也可称为恢复初始化状态 24 | */ 25 | protected int mHideStopMarginTop; 26 | 27 | public MoveView(Context context, AttributeSet attrs, int defStyleAttr) { 28 | 29 | super(context, attrs, defStyleAttr); 30 | } 31 | 32 | public MoveView(Context context, AttributeSet attrs) { 33 | 34 | super(context, attrs); 35 | } 36 | 37 | public MoveView(Context context) { 38 | 39 | super(context); 40 | } 41 | 42 | /** 43 | * 当前View是否可以出发TouchEvent事件 44 | * @return 默认不允许 45 | */ 46 | public boolean isTouchEventEnable() { 47 | 48 | return false; 49 | } 50 | 51 | /** 52 | * 获取此视图需要滑动的高度 53 | * @return 54 | */ 55 | protected int getNeedMoveHeight() { 56 | 57 | return mNeedMoveHeight; 58 | } 59 | 60 | /** 61 | * 设置此视图需要滑动的高度 62 | * @param needMoveHeight 63 | */ 64 | protected void setNeedMoveHeight(int needMoveHeight) { 65 | 66 | mNeedMoveHeight = needMoveHeight; 67 | } 68 | 69 | /** 70 | * 设置此视图向上滑动时,滑动停止时的marginTop值 71 | * @param showStopMarginTop 72 | */ 73 | protected void setShowStopMarginTop(int showStopMarginTop) { 74 | 75 | mShowStopMarginTop = showStopMarginTop; 76 | } 77 | 78 | /** 79 | * 设置此视图向下滑动时,滑动停止时的marginTop值 80 | * @param hideStopMarginTop 81 | */ 82 | protected void setHideStopMarginTop(int hideStopMarginTop) { 83 | 84 | mHideStopMarginTop = hideStopMarginTop; 85 | } 86 | 87 | /** 88 | * 或取此视图当前的marginTop值 89 | * @return 90 | */ 91 | protected int getMarginTop() { 92 | 93 | return ((MarginLayoutParams)getLayoutParams()).topMargin; 94 | } 95 | 96 | /** 97 | * 根据指定的step更新此视图的MarginTop值 98 | * @param step 此处的step值分正负,如果向marginTop减小,则step为负数 99 | */ 100 | protected void updateMarginTop(float step) { 101 | 102 | //将step转换为整数 103 | int intStep = step > 0 ? ((int)(step + 0.5f)) : -((int)(Math.abs(step) + 0.5f)); 104 | 105 | MarginLayoutParams layoutParams = (MarginLayoutParams)getLayoutParams(); 106 | layoutParams.topMargin = layoutParams.topMargin + intStep; 107 | setLayoutParams(layoutParams); 108 | invalidate(); 109 | } 110 | 111 | /** 112 | * 是否恢复完成 113 | * @return 114 | */ 115 | protected synchronized boolean isHideFinish() { 116 | 117 | if(mHideStopMarginTop < 0) { 118 | return getMarginTop() <= mHideStopMarginTop ? true : false; 119 | } else { 120 | return getMarginTop() >= mHideStopMarginTop ? true : false; 121 | } 122 | } 123 | 124 | /** 125 | * 是否展示完成 126 | * @return 127 | */ 128 | protected synchronized boolean isShowFinish() { 129 | 130 | if(mShowStopMarginTop > 0) { 131 | return getMarginTop() <= mShowStopMarginTop ? true : false; 132 | } else { 133 | return getMarginTop() >= mShowStopMarginTop ? true : false; 134 | } 135 | } 136 | 137 | 138 | /** 139 | * 获取当前实际可滑动的距离 140 | * @param step 可滑动距离,此距离值会与最大可滑动距离进行比较,取最小值 141 | * @return 142 | */ 143 | protected float getShowMoveStep(float step) { 144 | 145 | int maxStep = Math.abs(Math.abs(getMarginTop()) - Math.abs(mShowStopMarginTop));//此次滑动所允许滑动的最大step 146 | if(step > maxStep) {//可用滑动的marginTop小于step值,则用最大可滑动值替代 147 | step = maxStep; 148 | } 149 | return step; 150 | } 151 | 152 | /** 153 | * 获取当前实际可滑动的距离 154 | * @param step 可滑动距离,此距离值会与最大可滑动距离进行比较,取最小值 155 | * @return 156 | */ 157 | protected float getHideMoveStep(float step) { 158 | 159 | int maxStep = Math.abs(Math.abs(mHideStopMarginTop) - Math.abs(getMarginTop()));//此次滑动所允许滑动的最大step 160 | if(step > maxStep) {//可用滑动的marginTop小于step值,则用最大可滑动值替代 161 | step = maxStep; 162 | } 163 | return step; 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /app/src/main/java/cn/ittiger/ucpage/view/PageHeadView.java: -------------------------------------------------------------------------------- 1 | package cn.ittiger.ucpage.view; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | 6 | /** 7 | * 页面头部View(对应UC首页新闻视图向上滑动完成后页面的头部View,UC中最开始隐藏) 8 | * @author laohu 9 | * @link http://ittiger.cn 10 | * 11 | */ 12 | public class PageHeadView extends MoveView { 13 | 14 | public PageHeadView(Context context) { 15 | 16 | super(context); 17 | } 18 | 19 | public PageHeadView(Context context, AttributeSet attrs) { 20 | 21 | super(context, attrs); 22 | } 23 | 24 | public PageHeadView(Context context, AttributeSet attrs, int defStyleAttr) { 25 | 26 | super(context, attrs, defStyleAttr); 27 | } 28 | 29 | public synchronized void onShowAnimation(float step) { 30 | 31 | if(isShowFinish()) { 32 | return; 33 | } 34 | updateMarginTop(getShowMoveStep(step)); 35 | } 36 | 37 | public synchronized void onHideAnimation(float step) { 38 | 39 | if(isHideFinish()) { 40 | return; 41 | } 42 | updateMarginTop(-getHideMoveStep(step)); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/java/cn/ittiger/ucpage/view/PageNavigationView.java: -------------------------------------------------------------------------------- 1 | package cn.ittiger.ucpage.view; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | 6 | /** 7 | * UC首页的网址导航视图 8 | * 9 | * @author laohu 10 | * @link http://ittiger.cn 11 | */ 12 | public class PageNavigationView extends TouchMoveView { 13 | 14 | public PageNavigationView(Context context) { 15 | 16 | super(context); 17 | } 18 | 19 | public PageNavigationView(Context context, AttributeSet attrs) { 20 | 21 | super(context, attrs); 22 | } 23 | 24 | public PageNavigationView(Context context, AttributeSet attrs, 25 | int defStyleAttr) { 26 | 27 | super(context, attrs, defStyleAttr); 28 | } 29 | 30 | public synchronized void onShowAnimation(float step) { 31 | 32 | if(isShowFinish()) { 33 | return; 34 | } 35 | updateMarginTop(-getShowMoveStep(step)); 36 | } 37 | 38 | public synchronized void onHideAnimation(float step) { 39 | 40 | if(isHideFinish()) { 41 | return; 42 | } 43 | updateMarginTop(getHideMoveStep(step)); 44 | } 45 | 46 | @Override 47 | public synchronized boolean isShowFinish() { 48 | 49 | return getMarginTop() <= mShowStopMarginTop ? true : false; 50 | } 51 | 52 | @Override 53 | public synchronized boolean isHideFinish() { 54 | 55 | return getMarginTop() >= mHideStopMarginTop ? true : false; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/java/cn/ittiger/ucpage/view/TouchMoveView.java: -------------------------------------------------------------------------------- 1 | package cn.ittiger.ucpage.view; 2 | 3 | import android.content.Context; 4 | import android.text.method.Touch; 5 | import android.util.AttributeSet; 6 | import android.view.MotionEvent; 7 | 8 | public class TouchMoveView extends MoveView { 9 | 10 | private TouchMoveListener mTouchMoveListener; 11 | 12 | public TouchMoveView(Context context, AttributeSet attrs, int defStyleAttr) { 13 | super(context, attrs, defStyleAttr); 14 | } 15 | 16 | public TouchMoveView(Context context, AttributeSet attrs) { 17 | super(context, attrs); 18 | } 19 | 20 | public TouchMoveView(Context context) { 21 | super(context); 22 | } 23 | 24 | @Override 25 | public boolean isTouchEventEnable() { 26 | return true; 27 | } 28 | 29 | @Override 30 | public boolean onTouchEvent(MotionEvent event) { 31 | 32 | if(isTouchEventEnable()) { 33 | if(mTouchMoveListener != null) { 34 | mTouchMoveListener.onTouchMoveEvent(event); 35 | } 36 | } 37 | return isTouchEventEnable() || super.onTouchEvent(event); 38 | } 39 | 40 | 41 | public interface TouchMoveListener { 42 | 43 | void onTouchMoveEvent(MotionEvent event); 44 | } 45 | 46 | public void setTouchMoveListener(TouchMoveListener touchMoveListener) { 47 | 48 | this.mTouchMoveListener = touchMoveListener; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/java/cn/ittiger/ucpage/view/UCIndexView.java: -------------------------------------------------------------------------------- 1 | package cn.ittiger.ucpage.view; 2 | 3 | import cn.ittiger.ucpage.R; 4 | 5 | import android.content.Context; 6 | import android.content.res.TypedArray; 7 | import android.util.AttributeSet; 8 | import android.util.Log; 9 | import android.view.MotionEvent; 10 | import android.widget.FrameLayout; 11 | 12 | /** 13 | * 包含UC首页动画的自定义View 14 | * @author laohu 15 | */ 16 | public class UCIndexView extends FrameLayout implements TouchMoveView.TouchMoveListener { 17 | 18 | /** 19 | * 上下文 20 | */ 21 | private Context mContext; 22 | /** 23 | * 页面头部视图(对应UC首页新闻视图向上滑动完成后页面的头部View,UC中最开始隐藏) 24 | */ 25 | private PageHeadView mPageHeadView; 26 | /** 27 | * 页面内容导航视图(对应UC首页的网址导航视图) 28 | */ 29 | private PageNavigationView mPageNavigationView; 30 | /** 31 | * 内容视图的头部视图(对应UC首页新闻视图完全展示后的新闻类型导航头部部分,初始时隐藏) 32 | */ 33 | private ContentHeadView mContentHeadView; 34 | /** 35 | * 内容视图(对应UC首页中的新闻部分) 36 | */ 37 | private ContentView mContentView; 38 | 39 | 40 | /** 41 | * 页面头部视图是否固定(默认不固定),此属性为true的话PageHeadView则会固定在界面顶部不会滑动,此属性为false的话PageHeadView会首先隐藏再慢慢滑出 42 | */ 43 | private boolean mIsPageHeadViewFixed = false; 44 | /** 45 | * 内容视图的头部视图是否启用,默认启用 46 | */ 47 | private boolean mIsContentHeadViewEnable = true; 48 | /** 49 | * 是否允许下拉时恢复初始化状态,默认不允许 50 | */ 51 | private boolean mIsPullRestoreEnable = false; 52 | /** 53 | * 页面头部的高度 54 | */ 55 | private int mPageHeadViewHeight = 0; 56 | /** 57 | * 页面内容视图的头部视图的高度,当内容视图的头部视图不启用时,此值为0 58 | */ 59 | private int mContentHeadViewHeight = 0; 60 | /** 61 | * 手指放开后,视图自动滑动时的每次滑动距离间隔,默认每次滑动20px 62 | */ 63 | private int mAutoSlipStep = 20; 64 | /** 65 | * 手指放开后,视图自动滑动时的时间间隔,默认每间隔5毫秒滑动一次 66 | */ 67 | private int mAutoSlipTimeStep = 5; 68 | 69 | public UCIndexView(Context context) { 70 | 71 | super(context); 72 | this.init(context, null); 73 | } 74 | 75 | public UCIndexView(Context context, AttributeSet attrs) { 76 | 77 | super(context, attrs); 78 | this.init(context, attrs); 79 | } 80 | 81 | public UCIndexView(Context context, AttributeSet attrs, int defStyleAttr) { 82 | 83 | super(context, attrs, defStyleAttr); 84 | this.init(context, attrs); 85 | } 86 | 87 | private void init(Context context, AttributeSet attributeSet) { 88 | 89 | this.mContext = context; 90 | TypedArray typeArray = context.obtainStyledAttributes(attributeSet, R.styleable.UCIndexView); 91 | 92 | //页面头部的高度(对应UC首页新闻视图向上滑动完成后页面的头部View,UC中最开始隐藏) 93 | float pageHeadViewHeight = typeArray.getDimension(R.styleable.UCIndexView_pageHeadViewHeight, 0); 94 | mPageHeadViewHeight = (int)(pageHeadViewHeight + 0.5f); 95 | 96 | //页面头部是否固定,默认不固定(即默认隐藏,随着滑动而展示出来),如果固定的话则一直在头部展示不会随着滑动和动 97 | mIsPageHeadViewFixed = typeArray.getBoolean(R.styleable.UCIndexView_isPageHeadViewFixed, false); 98 | 99 | //页面头部的内容布局资源ID 100 | int pageHeadViewLayoutId = typeArray.getResourceId(R.styleable.UCIndexView_pageHeadViewLayoutId, 0); 101 | 102 | //页面内容导航的高度(对应UC首页的网址导航视图) 103 | float pageNavigationViewHeight = typeArray.getDimension(R.styleable.UCIndexView_pageNavigationViewHeight, 0); 104 | 105 | //页面内容导航的布局资源ID 106 | int pageNavigationViewLayoutId = typeArray.getResourceId(R.styleable.UCIndexView_pageNavigationViewLayoutId, 0); 107 | 108 | //内容视图的头部(对应UC首页新闻视图完全展示后的新闻类型导航头部部分,初始时隐藏) 109 | float contentHeadViewHeight = typeArray.getDimension(R.styleable.UCIndexView_contentHeadViewHeight, 0); 110 | mContentHeadViewHeight = (int)(contentHeadViewHeight + 0.5f); 111 | 112 | //内容视图的头部是否启用,默认启用 113 | mIsContentHeadViewEnable = typeArray.getBoolean(R.styleable.UCIndexView_isContentHeadViewEnable, true); 114 | 115 | //内容视图头部的布局资源ID 116 | int contentHeadViewLayoutId = typeArray.getResourceId(R.styleable.UCIndexView_contentHeadViewLayoutId, 0); 117 | 118 | //内容视图的布局资源ID 119 | int contentViewLayoutId = typeArray.getResourceId(R.styleable.UCIndexView_contentViewLayoutId, 0); 120 | 121 | mAutoSlipStep = (int)(typeArray.getDimension(R.styleable.UCIndexView_autoSlipDistanceStep, mAutoSlipStep) + 0.5f); 122 | mAutoSlipTimeStep = typeArray.getInt(R.styleable.UCIndexView_autoSlipTimeStep, mAutoSlipTimeStep); 123 | 124 | mIsPullRestoreEnable = typeArray.getBoolean(R.styleable.UCIndexView_isPullRestoreEnable, false); 125 | 126 | typeArray.recycle(); 127 | 128 | //下面四个视图的添加顺序不能更改,否则无法达到效果,此处视图的遮盖采用的是FrameLayout布局的特性 129 | addPageNavigationView(pageNavigationViewLayoutId, pageNavigationViewHeight); 130 | addPageHeadView(pageHeadViewLayoutId, pageHeadViewHeight); 131 | addContentHeadView(contentHeadViewLayoutId, contentHeadViewHeight, pageNavigationViewHeight); 132 | addContentView(contentViewLayoutId, pageNavigationViewHeight); 133 | } 134 | 135 | /** 136 | * 添加页面头部视图 137 | * @param resId 页面头部视图中的元素布局资源文件ID 138 | * @param pageHeadViewHeight 页面头部视图的高度 139 | */ 140 | private void addPageHeadView(int resId, float pageHeadViewHeight) { 141 | 142 | if(resId == 0) { 143 | throw new IllegalArgumentException("please set attribution pageHeadViewLayoutId int UCIndexView"); 144 | } 145 | 146 | mPageHeadView = new PageHeadView(mContext); 147 | LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, (int)(pageHeadViewHeight + 0.5f)); 148 | if(!mIsPageHeadViewFixed) {//不固定头部,头部会进行隐藏 149 | layoutParams.topMargin = -(int)(pageHeadViewHeight + 0.5f); 150 | } 151 | mPageHeadView.setLayoutParams(layoutParams); 152 | 153 | mPageHeadView.setShowStopMarginTop(0);//向上滑时PageHeadView的滑动停止的位置,该位置应该是PageHeadView刚好展示在页面顶部的位置,此时marginTop为0 154 | mPageHeadView.setHideStopMarginTop(layoutParams.topMargin);//向下滑时PageHeadView的滑动停止的位置,此时该位置为PageHeadView的初始化位置 155 | mPageHeadView.setNeedMoveHeight(mPageHeadViewHeight);//设置ContentHeadView相对ContentView需要滑动的高度,即为其本身的高度 156 | 157 | inflate(mContext, resId, mPageHeadView); 158 | addView(mPageHeadView); 159 | } 160 | 161 | /** 162 | * 添加页面内容导航视图 163 | * @param resId 页面内容导航视图的元素布局资源文件ID 164 | * @param pageNavigationViewHeight 页面内容导航视图的高度 165 | */ 166 | private void addPageNavigationView(int resId, float pageNavigationViewHeight) { 167 | 168 | if(resId == 0) { 169 | throw new IllegalArgumentException("please set attribution pageNavigationViewLayoutId int UCIndexView"); 170 | } 171 | mPageNavigationView = new PageNavigationView(mContext); 172 | if(mIsPageHeadViewFixed) {//页面头部固定时,将PageNavigationView的PadingTop设置为PageHeadView的高度,防止PageHeadView将PageNavigationView的部分内容遮住 173 | mPageNavigationView.setPadding(0, mPageHeadViewHeight, 0, 0); 174 | } 175 | LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, (int)(pageNavigationViewHeight + 0.5f)); 176 | mPageNavigationView.setLayoutParams(layoutParams); 177 | mPageNavigationView.setTouchMoveListener(this); 178 | 179 | //向上滑动时PageNavigationView的停止位置设置为PageHeadView高度的一半 180 | mPageNavigationView.setShowStopMarginTop(-mPageHeadViewHeight / 2); 181 | //PageNavigationView的滑动距离为PageHeadView高度的一半 182 | mPageNavigationView.setNeedMoveHeight(mPageHeadViewHeight / 2); 183 | mPageNavigationView.setHideStopMarginTop(layoutParams.topMargin); 184 | 185 | inflate(mContext, resId, mPageNavigationView); 186 | addView(mPageNavigationView); 187 | } 188 | 189 | /** 190 | * 添加内容视图的头部视图 191 | * @param resId 页面内容视图中的头部视图的元素布局资源文件ID 192 | * @param contentHeadViewHeight 页面内容视图中的头部视图的高度 193 | * @param marginTop 页面内容视图中的头部视图的marginTop 194 | */ 195 | private void addContentHeadView(int resId, float contentHeadViewHeight, float marginTop) { 196 | 197 | if(!mIsContentHeadViewEnable) {//内容头部视图不启用 198 | mContentHeadViewHeight = 0; 199 | return; 200 | } 201 | if(resId == 0) { 202 | throw new IllegalArgumentException("please set attribution contentHeadViewLayoutId int UCIndexView"); 203 | } 204 | mContentHeadView = new ContentHeadView(mContext); 205 | LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, (int)(contentHeadViewHeight + 0.5f)); 206 | layoutParams.topMargin = (int)(marginTop + 0.5f); 207 | mContentHeadView.setLayoutParams(layoutParams); 208 | 209 | mContentHeadView.setShowStopMarginTop(mPageHeadViewHeight);//向上滑时ContentHeadView的滑动停止的位置,该位置应该是刚好距离一个PageHeadView的高度 210 | mContentHeadView.setHideStopMarginTop(layoutParams.topMargin);//向下滑时ContentHeadView的滑动停止的位置,此时该位置为ContentHeadView的初始化位置 211 | mContentHeadView.setNeedMoveHeight((int)(contentHeadViewHeight + 0.5f));//设置ContentHeadView相对ContentView需要滑动的高度,即为其本身的高度 212 | 213 | inflate(mContext, resId, mContentHeadView); 214 | addView(mContentHeadView); 215 | } 216 | 217 | /** 218 | * 添加内容视图 219 | * @param resId 页面内容视图的元素布局资源文件ID 220 | * @param marginTop 页面内容视图的marginTop 221 | */ 222 | private void addContentView(int resId, float marginTop) { 223 | 224 | if(resId == 0) { 225 | throw new IllegalArgumentException("please set attribution contentViewLayoutId int UCIndexView"); 226 | } 227 | 228 | mContentView = new ContentView(mContext); 229 | LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); 230 | layoutParams.topMargin = (int)(marginTop + 0.5f); 231 | mContentView.setLayoutParams(layoutParams); 232 | mContentView.setTouchMoveListener(this); 233 | 234 | //向上滑时ContentView的滑动停止的位置,该位置应该是PageHeadView和ContentHeadView刚好展示在页面顶部的位置,此时marginTop为PageHeadView和ContentHeadView两者的高度之和 235 | mContentView.setShowStopMarginTop(mContentHeadViewHeight + mPageHeadViewHeight); 236 | mContentView.setHideStopMarginTop(layoutParams.topMargin);//向下滑时ContentView的滑动停止的位置,此时该位置为ContentView的初始化位置 237 | //ContentView需要移动的距离为其本身到顶部的MarginTop值减去ContentHeadView和PageHeadView两者的高度 238 | mContentView.setNeedMoveHeight(Math.abs(layoutParams.topMargin - mContentHeadViewHeight - mPageHeadViewHeight)); 239 | 240 | inflate(mContext, resId, mContentView); 241 | addView(mContentView); 242 | } 243 | 244 | /** 245 | * 上一次的触摸点Y坐标 246 | */ 247 | private float mLastTouchY = 0; 248 | /** 249 | * 两次触摸点之间的距离 250 | */ 251 | private float mDelY = 0; 252 | 253 | @Override 254 | public void onTouchMoveEvent(MotionEvent event) { 255 | 256 | switch (event.getAction()) { 257 | case MotionEvent.ACTION_DOWN: 258 | mLastTouchY = event.getRawY(); 259 | break; 260 | case MotionEvent.ACTION_MOVE: 261 | mDelY = event.getRawY() - mLastTouchY; 262 | viewMove(mDelY, mIsPullRestoreEnable); 263 | mLastTouchY = event.getRawY(); 264 | break; 265 | case MotionEvent.ACTION_UP: 266 | int offset = 0; 267 | if(mDelY > 0) {//hide,下拉 268 | if(!mIsPullRestoreEnable) {//当前不允许下拉恢复 269 | return; 270 | } 271 | offset = mContentView.getHideOffset(); 272 | } else {//show, 上拉 273 | offset = mContentView.getShowOffset(); 274 | } 275 | if(offset <= mPageHeadView.getNeedMoveHeight() / 2) {//没有滑过二分之一高度 276 | slip(-mDelY, mIsPullRestoreEnable); 277 | } else { 278 | slip(mDelY, mIsPullRestoreEnable); 279 | } 280 | break; 281 | } 282 | } 283 | 284 | /** 285 | * 手指松开屏幕后,视图自动滑动 286 | * 每隔 mAutoSlipTimeStep 长时间滑动 mAutoSlipStep 距离 287 | * @param delY 当前的滑动距离 288 | * @param isPullRestoreEnable 是否允许下拉恢复 289 | */ 290 | private void slip(float delY, final boolean isPullRestoreEnable) { 291 | 292 | if(delY > 0) {//当前滑动为向下滑动,即处于恢复状态 293 | if(isHideFinish()) {//已经恢复结束 294 | return; 295 | } 296 | postDelayed(new Runnable() { 297 | @Override 298 | public void run() { 299 | 300 | viewMove(mAutoSlipStep, isPullRestoreEnable); 301 | slip(mAutoSlipStep, isPullRestoreEnable);//准备下一次滑动 302 | } 303 | }, mAutoSlipTimeStep); 304 | } else {//当前滑动为向上滑动,即处于展示状态 305 | if(isShowFinish()) {//已经展示结束 306 | return; 307 | } 308 | postDelayed(new Runnable() { 309 | @Override 310 | public void run() { 311 | 312 | viewMove(-mAutoSlipStep, isPullRestoreEnable); 313 | slip(-mAutoSlipStep, isPullRestoreEnable);//准备下一次滑动 314 | } 315 | }, mAutoSlipTimeStep); 316 | } 317 | } 318 | 319 | /** 320 | * 对所有视图进行滑动操作 321 | * @param delY 当前的滑动步长 322 | * @param isPullRestoreEnable 是否允许下拉恢复 323 | */ 324 | private void viewMove(float delY, boolean isPullRestoreEnable) { 325 | 326 | float step = Math.abs(delY); 327 | //根据滑动距离的比例计算PageHeadView的滑动步长 328 | float pageHeadViewStep = step * mPageHeadView.getNeedMoveHeight() / mContentView.getNeedMoveHeight(); 329 | //手指滑动距离作为ContentView的滑动步长 330 | float contentViewStep = step; 331 | //ContentHeadView初始不固定显示时,其实际滑动步长为ContentView的滑动步长加上其相对ContentView的滑动步长 332 | float contentHeadViewStep = mIsContentHeadViewEnable ? step + step * mContentHeadView.getNeedMoveHeight() / mContentView.getNeedMoveHeight() : 0; 333 | float pageNavigationViewStep = step * mPageNavigationView.getNeedMoveHeight() / mContentView.getNeedMoveHeight(); 334 | 335 | if(delY > 0) {//下滑 336 | if(!isPullRestoreEnable) {//当前不允许下拉恢复 337 | return; 338 | } 339 | if(!isHideFinish()) {//恢复状态是否已完成 340 | if(mIsPageHeadViewFixed == false) { 341 | mPageHeadView.onHideAnimation(pageHeadViewStep); 342 | } 343 | mContentView.onHideAnimation(contentViewStep); 344 | if(mIsContentHeadViewEnable) { 345 | mContentHeadView.onHideAnimation(contentHeadViewStep); 346 | } 347 | mPageNavigationView.onHideAnimation(pageNavigationViewStep); 348 | } 349 | } else {//上滑 350 | if(!isShowFinish()) {//展示状态是否已完成 351 | if(mIsPageHeadViewFixed == false) {//PageHeadView没有被固定时才进行滑动 352 | mPageHeadView.onShowAnimation(pageHeadViewStep); 353 | } 354 | mContentView.onShowAnimation(contentViewStep); 355 | if(mIsContentHeadViewEnable) {//ContentHeadView启用时才进行滑动 356 | mContentHeadView.onShowAnimation(contentHeadViewStep); 357 | } 358 | mPageNavigationView.onShowAnimation(pageNavigationViewStep); 359 | } 360 | } 361 | } 362 | 363 | /** 364 | * 判断所有视图是否全部恢复结束 365 | * @return 366 | */ 367 | private boolean isHideFinish() { 368 | 369 | //当PageHeadView固定的时候,默认为恢复结束 370 | boolean pageHeadViewHideFinish = mIsPageHeadViewFixed ? true : mPageHeadView.isHideFinish(); 371 | //当ContentHeadView不启用的时候,默认为恢复结束 372 | boolean contentHeadViewHideFinish = mIsContentHeadViewEnable ? mContentHeadView.isHideFinish() : true; 373 | 374 | return pageHeadViewHideFinish && mPageNavigationView.isHideFinish() && 375 | contentHeadViewHideFinish && mContentView.isHideFinish(); 376 | } 377 | 378 | /** 379 | * 判断所有视图是否全部展示结束 380 | * @return 381 | */ 382 | private boolean isShowFinish() { 383 | 384 | //当PageHeadView固定的时候,默认为展示结束 385 | boolean pageHeadViewShowFinish = mIsPageHeadViewFixed ? true : mPageHeadView.isShowFinish(); 386 | //当ContentHeadView不启用的时候,默认为展示结束 387 | boolean contentHeadViewShowFinish = mIsContentHeadViewEnable ? mContentHeadView.isShowFinish() : true; 388 | 389 | return pageHeadViewShowFinish && mPageNavigationView.isShowFinish() && 390 | contentHeadViewShowFinish && mContentView.isShowFinish(); 391 | } 392 | 393 | /** 394 | * 恢复初始化状态 395 | */ 396 | public void onBackRestore() { 397 | slip(1, true); 398 | } 399 | 400 | public boolean isPullRestoreEnable() { 401 | 402 | return mIsPullRestoreEnable; 403 | } 404 | 405 | public void setPullRestoreEnable(boolean isPullRestoreEnable) { 406 | 407 | mIsPullRestoreEnable = isPullRestoreEnable; 408 | } 409 | } 410 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_uc_index_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/layout/content_head_view_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/layout/content_view_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/layout/page_head_view_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/layout/page_navigation_view_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_main.xml: -------------------------------------------------------------------------------- 1 | 5 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huyongli/UCIndexAnimation/a0713c25b897bff9184c260ede4a0708a35051e8/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huyongli/UCIndexAnimation/a0713c25b897bff9184c260ede4a0708a35051e8/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huyongli/UCIndexAnimation/a0713c25b897bff9184c260ede4a0708a35051e8/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huyongli/UCIndexAnimation/a0713c25b897bff9184c260ede4a0708a35051e8/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huyongli/UCIndexAnimation/a0713c25b897bff9184c260ede4a0708a35051e8/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/values-v21/styles.xml: -------------------------------------------------------------------------------- 1 | > 2 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10dp 6 | 40dp 7 | -40dp 8 | 200dp 9 | 16dp 10 | 16dp 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | UCPageDemo 5 | UC头条 6 | NaviContent_网址导航区 7 | 新闻头部导航 8 | 新闻内容区 9 | StartActivity 10 | Hello world! 11 | Settings 12 | DemoActivity 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 14 |