├── .gitignore ├── .idea ├── .name ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── gradle.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── fe │ │ └── wdj │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── fe │ │ │ └── wdj │ │ │ ├── activity │ │ │ ├── DetailActivity.java │ │ │ └── MainActivity.java │ │ │ ├── adapter │ │ │ └── MyAdapter.java │ │ │ ├── util │ │ │ ├── BigDecimalUtils.java │ │ │ └── DisplayUtils.java │ │ │ └── widget │ │ │ ├── DetailScrollView.java │ │ │ ├── DividerItemDecoration.java │ │ │ └── SVRootLinearLayout.java │ └── res │ │ ├── anim │ │ └── activity_out.xml │ │ ├── drawable-xxhdpi │ │ ├── hg.jpg │ │ ├── ic_aqy.png │ │ ├── ic_bb.png │ │ ├── ic_cz.png │ │ ├── ic_kk.png │ │ ├── ic_kr.png │ │ ├── ic_qq.png │ │ ├── ic_sg.png │ │ ├── ic_xl.png │ │ ├── ic_yk.png │ │ ├── ic_yyy.png │ │ ├── mv.jpg │ │ ├── xg.gif │ │ └── zy.jpg │ │ ├── layout │ │ ├── activity_app_detail.xml │ │ ├── activity_detail.xml │ │ ├── activity_detail_bottom.xml │ │ ├── activity_detail_title.xml │ │ ├── activity_list_item.xml │ │ └── activity_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-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── fe │ └── wdj │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | WdjAppDetail -------------------------------------------------------------------------------- /.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/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 23 | -------------------------------------------------------------------------------- /.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 | 47 | 48 | 49 | 50 | 1.7 51 | 52 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /.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 | # WdjAppDetail 2 | Andoroid 模仿豌豆荚应用列表跳转详情界面特效 3 | 4 | ![image](https://github.com/chenpengfei88/WdjAppDetail/blob/master/app/src/main/res/drawable-xxhdpi/xg.gif) 5 | -------------------------------------------------------------------------------- /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 "com.fe.wdj" 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:recyclerview-v7:22.2.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 C:\Users\lenovo\AppData\Local\Android\sdk/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/com/fe/wdj/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.fe.wdj; 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 | 4 | 5 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/java/com/fe/wdj/activity/DetailActivity.java: -------------------------------------------------------------------------------- 1 | package com.fe.wdj.activity; 2 | 3 | import android.animation.Animator; 4 | import android.animation.AnimatorListenerAdapter; 5 | import android.animation.ValueAnimator; 6 | import android.content.Intent; 7 | import android.graphics.drawable.ColorDrawable; 8 | import android.graphics.drawable.Drawable; 9 | import android.os.Bundle; 10 | import android.support.v7.app.AppCompatActivity; 11 | import android.view.KeyEvent; 12 | import android.view.View; 13 | import android.view.ViewTreeObserver; 14 | import android.widget.ImageView; 15 | import android.widget.LinearLayout; 16 | import android.widget.TextView; 17 | import com.fe.wdj.util.DisplayUtils; 18 | import com.fe.wdj.R; 19 | import com.fe.wdj.widget.DetailScrollView; 20 | import com.fe.wdj.widget.SVRootLinearLayout; 21 | 22 | /** 23 | * Created by chenpengfei on 2016/11/23. 24 | */ 25 | public class DetailActivity extends AppCompatActivity { 26 | 27 | 28 | private DetailScrollView mScrollView; 29 | 30 | //ScrollView 底部的布局LinearLayout 31 | private SVRootLinearLayout mSVRootLl; 32 | 33 | //app图片和名字控件 34 | private ImageView mIconImageView; 35 | private TextView mAppNameTextView; 36 | 37 | //中间内容布局,内容里的底部和顶部title布局 38 | private LinearLayout mContentLl; 39 | //内容里的底部和顶部title布局 40 | private LinearLayout mBottomLl, mTitleLl; 41 | 42 | //根布局的背景色 43 | private ColorDrawable mRootCDrawable; 44 | private int mColorInitAlpha = 150; 45 | 46 | private int mContentTopOffsetNum; 47 | private int mContentBottomOffsetNum; 48 | private int mImageLeftOffsetNum; 49 | private int mImageTopOffsetNum; 50 | 51 | //接收过来的参数 52 | private int mViewMarginTop; 53 | private int mImageId; 54 | private String mAppName; 55 | 56 | private boolean initData; 57 | 58 | 59 | 60 | @Override 61 | protected void onCreate(Bundle savedInstanceState) { 62 | super.onCreate(savedInstanceState); 63 | setContentView(R.layout.activity_detail); 64 | 65 | DisplayUtils.hideActionBar(getWindow()); 66 | receiveParams(); 67 | initView(); 68 | initAnimationData(); 69 | } 70 | 71 | private void initView() { 72 | //设置root节点view的背景透明度 73 | LinearLayout rootLl = (LinearLayout) findViewById(R.id.ll_root); 74 | Drawable rootBgDrawable = rootLl.getBackground(); 75 | mRootCDrawable = (ColorDrawable) rootBgDrawable; 76 | mRootCDrawable.setAlpha(mColorInitAlpha); 77 | 78 | mScrollView = (DetailScrollView) findViewById(R.id.scrollView); 79 | mSVRootLl = (SVRootLinearLayout) findViewById(R.id.ll_sv_root); 80 | mIconImageView = (ImageView) findViewById(R.id.imageview_icon); 81 | mIconImageView.setImageResource(mImageId); 82 | 83 | mContentLl = (LinearLayout) findViewById(R.id.ll_content); 84 | mBottomLl = (LinearLayout) findViewById(R.id.ll_bottom); 85 | mAppNameTextView = (TextView) findViewById(R.id.textview_appname); 86 | mAppNameTextView.setText(mAppName); 87 | mTitleLl = (LinearLayout) findViewById(R.id.ll_title); 88 | 89 | mImageTopOffsetNum = getResources().getDimensionPixelOffset(R.dimen.title_view_height); 90 | 91 | //设置初始化的位置 92 | mSVRootLl.setContentInitMarginTop(mViewMarginTop); 93 | mContentTopOffsetNum = mViewMarginTop - getResources().getDimensionPixelOffset(R.dimen.view_height); 94 | 95 | /** 96 | * activity 关闭回调 97 | */ 98 | mSVRootLl.setOnCloseListener(new SVRootLinearLayout.OnCloseListener() { 99 | @Override 100 | public void onClose() { 101 | finish(); 102 | overridePendingTransition(0, 0); 103 | } 104 | }); 105 | 106 | /** 107 | * 下拉拖动时候回调修改root背景色的透明度 108 | */ 109 | mSVRootLl.setOnUpdateBgColorListener(new SVRootLinearLayout.OnUpdateBgColorListener() { 110 | @Override 111 | public void onUpdate(float ratio) { 112 | mRootCDrawable.setAlpha((int) (mColorInitAlpha - mColorInitAlpha * ratio)); 113 | } 114 | }); 115 | } 116 | 117 | private void receiveParams() { 118 | Intent intent = getIntent(); 119 | mViewMarginTop = intent.getIntExtra("viewMarginTop", 0); 120 | mImageId = intent.getIntExtra("imageId", 0); 121 | mAppName = intent.getStringExtra("appName"); 122 | } 123 | 124 | private void initAnimationData() { 125 | mScrollView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { 126 | @Override 127 | public void onGlobalLayout() { 128 | if(!initData) { 129 | mContentBottomOffsetNum = mScrollView.getMeasuredHeight() - mContentLl.getBottom(); 130 | mSVRootLl.setInitBottom(mContentLl.getBottom()); 131 | mSVRootLl.setAnimationStatus(true); 132 | mSVRootLl.setLayoutImageView(true); 133 | mImageLeftOffsetNum = (DisplayUtils.getScreenWidth(DetailActivity.this) - mIconImageView.getWidth()) / 2 - getResources().getDimensionPixelOffset(R.dimen.icon_margin); 134 | initData = true; 135 | startAnimation(); 136 | } 137 | } 138 | }); 139 | } 140 | 141 | private void startAnimation() { 142 | ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1).setDuration(400); 143 | valueAnimator.setStartDelay(100); 144 | valueAnimator.start(); 145 | valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 146 | @Override 147 | public void onAnimationUpdate(ValueAnimator animation) { 148 | float ratio = (float) animation.getAnimatedValue(); 149 | //内容布局顶部偏移量 150 | int contentTopOffset = (int) (ratio * mContentTopOffsetNum); 151 | //内容布局底部偏移量 152 | int contentBottomOffset = (int) (ratio * mContentBottomOffsetNum); 153 | //图片左边偏移量 154 | int imageLeftOffset = (int) (ratio * mImageLeftOffsetNum); 155 | //图片上边偏移量 156 | int imageTopOffset = (int) (ratio * mImageTopOffsetNum); 157 | mSVRootLl.setAllViewOffset(mViewMarginTop - contentTopOffset, contentBottomOffset, imageLeftOffset, imageTopOffset); 158 | } 159 | }); 160 | 161 | valueAnimator.addListener(new AnimatorListenerAdapter() { 162 | @Override 163 | public void onAnimationEnd(Animator animation) { 164 | super.onAnimationEnd(animation); 165 | mSVRootLl.setAnimationStatus(false); 166 | mBottomLl.setVisibility(View.VISIBLE); 167 | mTitleLl.setVisibility(View.VISIBLE); 168 | } 169 | }); 170 | } 171 | 172 | 173 | @Override 174 | public boolean onKeyDown(int keyCode, KeyEvent event) { 175 | if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) { //按下的如果是BACK,同时没有重复 176 | if(mSVRootLl != null) mSVRootLl.startAnimation(mSVRootLl.getCenterVisibleViewHeight(), false, 0); 177 | return true; 178 | } 179 | return super.onKeyDown(keyCode, event); 180 | } 181 | 182 | } 183 | -------------------------------------------------------------------------------- /app/src/main/java/com/fe/wdj/activity/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.fe.wdj.activity; 2 | 3 | import android.content.Intent; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.os.Bundle; 6 | import android.support.v7.widget.LinearLayoutManager; 7 | import android.support.v7.widget.RecyclerView; 8 | import android.view.View; 9 | import com.fe.wdj.util.DisplayUtils; 10 | import com.fe.wdj.widget.DividerItemDecoration; 11 | import com.fe.wdj.R; 12 | import com.fe.wdj.adapter.MyAdapter; 13 | 14 | /** 15 | * Created by chenpengfei on 2016/11/23. 16 | */ 17 | public class MainActivity extends AppCompatActivity { 18 | 19 | private MyAdapter mMyAdapter; 20 | private RecyclerView mRecyclerView; 21 | 22 | private Object[][] mImageTextArray = new Object[][] {{R.drawable.ic_aqy, "爱奇艺"}, {R.drawable.ic_bb, "哔哩哔哩"}, 23 | {R.drawable.ic_cz, "赤足"}, {R.drawable.ic_kk, "快看"}, 24 | {R.drawable.ic_kr, "kingRoot"}, {R.drawable.ic_sg, "搜狗"}, 25 | {R.drawable.ic_xl, "迅雷"}, {R.drawable.ic_yk, "优酷"}, 26 | {R.drawable.ic_yyy, "网易云音乐"}, {R.drawable.ic_qq, "QQ"}}; 27 | 28 | @Override 29 | protected void onCreate(Bundle savedInstanceState) { 30 | super.onCreate(savedInstanceState); 31 | setContentView(R.layout.activity_main); 32 | DisplayUtils.hideActionBar(getWindow()); 33 | initView(); 34 | fillData(); 35 | } 36 | 37 | private void initView() { 38 | mRecyclerView = (RecyclerView) findViewById(R.id.recyclerview); 39 | mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); 40 | mRecyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL_LIST)); 41 | } 42 | 43 | private void fillData() { 44 | mMyAdapter = new MyAdapter(this, mImageTextArray); 45 | mRecyclerView.setAdapter(mMyAdapter); 46 | mMyAdapter.setOnItemClickLitener(new MyAdapter.OnItemClickLitener() { 47 | @Override 48 | public void onItemClick(View view, int position) { 49 | Object[] array = mImageTextArray[position]; 50 | int viewMarginTop = view.getTop() + getResources().getDimensionPixelOffset(R.dimen.bar_view_height); 51 | Intent intent = new Intent(MainActivity.this, DetailActivity.class); 52 | intent.putExtra("viewMarginTop", viewMarginTop); 53 | intent.putExtra("imageId", (int) array[0]); 54 | intent.putExtra("appName", (String) array[1]); 55 | startActivity(intent); 56 | overridePendingTransition(0, 0); 57 | } 58 | }); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /app/src/main/java/com/fe/wdj/adapter/MyAdapter.java: -------------------------------------------------------------------------------- 1 | package com.fe.wdj.adapter; 2 | 3 | import android.app.Activity; 4 | import android.support.v7.widget.RecyclerView; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.ImageView; 8 | import android.widget.TextView; 9 | 10 | import com.fe.wdj.R; 11 | 12 | /** 13 | * Created by chenpengfei on 2016/10/27. 14 | */ 15 | public class MyAdapter extends RecyclerView.Adapter { 16 | 17 | private Activity mActivity; 18 | private Object[][]mArray; 19 | private OnItemClickLitener mOnItemClickLitener; 20 | 21 | public MyAdapter(Activity activity, Object[][] array) { 22 | mActivity = activity; 23 | mArray = array; 24 | } 25 | 26 | @Override 27 | public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 28 | return new ItemViewHolder(View.inflate(mActivity, R.layout.activity_list_item, null)); 29 | } 30 | 31 | @Override 32 | public int getItemCount() { 33 | return mArray.length; 34 | } 35 | 36 | 37 | @Override 38 | public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) { 39 | ItemViewHolder itemVh = (ItemViewHolder) holder; 40 | Object[] contentArray = mArray[position]; 41 | itemVh.iconImageView.setImageResource((int) contentArray[0]); 42 | itemVh.appNameTextView.setText((String) contentArray[1]); 43 | if(mOnItemClickLitener != null) { 44 | itemVh.itemView.setOnClickListener(new View.OnClickListener() { 45 | @Override 46 | public void onClick(View v) { 47 | mOnItemClickLitener.onItemClick(v, position); 48 | } 49 | }); 50 | } 51 | } 52 | 53 | class ItemViewHolder extends RecyclerView.ViewHolder { 54 | 55 | ImageView iconImageView; 56 | TextView appNameTextView; 57 | 58 | public ItemViewHolder(View view) { 59 | super(view); 60 | iconImageView = (ImageView) view.findViewById(R.id.imageview_icon); 61 | appNameTextView = (TextView) view.findViewById(R.id.textview_appname); 62 | } 63 | } 64 | 65 | public void setOnItemClickLitener(OnItemClickLitener mOnItemClickLitener) { 66 | this.mOnItemClickLitener = mOnItemClickLitener; 67 | } 68 | 69 | public interface OnItemClickLitener { 70 | void onItemClick(View view, int position); 71 | } 72 | 73 | 74 | } 75 | -------------------------------------------------------------------------------- /app/src/main/java/com/fe/wdj/util/BigDecimalUtils.java: -------------------------------------------------------------------------------- 1 | package com.fe.wdj.util; 2 | 3 | import java.math.BigDecimal; 4 | 5 | /** 6 | * Created by chenpengfei on 2016/11/23. 7 | */ 8 | public class BigDecimalUtils { 9 | 10 | public static float divide(int one, int two) { 11 | return BigDecimal.valueOf(one).divide(BigDecimal.valueOf(two), 2, BigDecimal.ROUND_HALF_UP).floatValue(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/com/fe/wdj/util/DisplayUtils.java: -------------------------------------------------------------------------------- 1 | package com.fe.wdj.util; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.graphics.Color; 6 | import android.os.Build; 7 | import android.view.View; 8 | import android.view.Window; 9 | import android.view.WindowManager; 10 | 11 | /** 12 | * Created by lenovo on 2016/11/21. 13 | */ 14 | public class DisplayUtils { 15 | 16 | public static int dp2px(Context context, int dpValue) { 17 | float scale = context.getResources().getDisplayMetrics().density; 18 | return (int) (dpValue * scale + 0.5f); 19 | } 20 | 21 | public static int getScreenWidth(Context context) { 22 | return context.getResources().getDisplayMetrics().widthPixels; 23 | } 24 | 25 | public static int getScreenHeight(Context context) { 26 | return context.getResources().getDisplayMetrics().heightPixels; 27 | } 28 | 29 | public static void hideActionBar(Window window) { 30 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {//5.0及以上 31 | View decorView = window.getDecorView(); 32 | int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 33 | | View.SYSTEM_UI_FLAG_LAYOUT_STABLE; 34 | decorView.setSystemUiVisibility(option); 35 | window.setStatusBarColor(Color.TRANSPARENT); 36 | } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {//4.4到5.0 37 | WindowManager.LayoutParams localLayoutParams = window.getAttributes(); 38 | localLayoutParams.flags = (WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS | localLayoutParams.flags); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/src/main/java/com/fe/wdj/widget/DetailScrollView.java: -------------------------------------------------------------------------------- 1 | package com.fe.wdj.widget; 2 | 3 | import android.annotation.TargetApi; 4 | import android.content.Context; 5 | import android.os.Build; 6 | import android.util.AttributeSet; 7 | import android.view.MotionEvent; 8 | import android.view.ViewConfiguration; 9 | import android.widget.ScrollView; 10 | 11 | /** 12 | * Created by chenpengfei on 2016/11/22. 13 | */ 14 | public class DetailScrollView extends ScrollView { 15 | 16 | private float mInitY; 17 | private int mTouchSlop; 18 | private SVRootLinearLayout mSVRootLl; 19 | 20 | public DetailScrollView(Context context) { 21 | super(context); 22 | } 23 | 24 | public DetailScrollView(Context context, AttributeSet attrs) { 25 | super(context, attrs); 26 | mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); 27 | } 28 | 29 | public DetailScrollView(Context context, AttributeSet attrs, int defStyleAttr) { 30 | super(context, attrs, defStyleAttr); 31 | } 32 | 33 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 34 | public DetailScrollView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 35 | super(context, attrs, defStyleAttr, defStyleRes); 36 | } 37 | 38 | @Override 39 | protected void onFinishInflate() { 40 | super.onFinishInflate(); 41 | mSVRootLl = (SVRootLinearLayout) getChildAt(0); 42 | } 43 | 44 | @Override 45 | public boolean onTouchEvent(MotionEvent ev) { 46 | System.out.println("=========ScrollView=========onTouchEvent===========================" + ev.getAction()); 47 | return super.onTouchEvent(ev); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/java/com/fe/wdj/widget/DividerItemDecoration.java: -------------------------------------------------------------------------------- 1 | package com.fe.wdj.widget; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.graphics.Canvas; 6 | import android.graphics.Rect; 7 | import android.graphics.drawable.Drawable; 8 | import android.support.v7.widget.LinearLayoutManager; 9 | import android.support.v7.widget.RecyclerView; 10 | import android.view.View; 11 | 12 | /** 13 | * Created by chenpengfei on 2016/11/21. 14 | */ 15 | public class DividerItemDecoration extends RecyclerView.ItemDecoration { 16 | 17 | private static final int[] ATTRS = new int[] {android.R.attr.listDivider}; 18 | 19 | public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL; 20 | 21 | public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL; 22 | 23 | private Drawable mDivider; 24 | 25 | private int mOrientation; 26 | 27 | public DividerItemDecoration(Context context, int orientation) { 28 | final TypedArray a = context.obtainStyledAttributes(ATTRS); 29 | mDivider = a.getDrawable(0); 30 | a.recycle(); 31 | setOrientation(orientation); 32 | } 33 | 34 | public void setOrientation(int orientation) { 35 | if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) { 36 | throw new IllegalArgumentException("invalid orientation"); 37 | } 38 | mOrientation = orientation; 39 | } 40 | 41 | @Override 42 | public void onDraw(Canvas c, RecyclerView parent) { 43 | if (mOrientation == VERTICAL_LIST) { 44 | drawVertical(c, parent); 45 | } else { 46 | drawHorizontal(c, parent); 47 | } 48 | 49 | } 50 | 51 | 52 | public void drawVertical(Canvas c, RecyclerView parent) { 53 | final int left = parent.getPaddingLeft(); 54 | final int right = parent.getWidth() - parent.getPaddingRight(); 55 | 56 | final int childCount = parent.getChildCount(); 57 | for (int i = 0; i < childCount; i++) { 58 | final View child = parent.getChildAt(i); 59 | android.support.v7.widget.RecyclerView v = new android.support.v7.widget.RecyclerView(parent.getContext()); 60 | final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child 61 | .getLayoutParams(); 62 | final int top = child.getBottom() + params.bottomMargin; 63 | final int bottom = top + mDivider.getIntrinsicHeight(); 64 | mDivider.setBounds(left, top, right, bottom - 1); 65 | mDivider.draw(c); 66 | } 67 | } 68 | 69 | public void drawHorizontal(Canvas c, RecyclerView parent) { 70 | final int top = parent.getPaddingTop(); 71 | final int bottom = parent.getHeight() - parent.getPaddingBottom(); 72 | 73 | final int childCount = parent.getChildCount(); 74 | for (int i = 0; i < childCount; i++) { 75 | final View child = parent.getChildAt(i); 76 | final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child 77 | .getLayoutParams(); 78 | final int left = child.getRight() + params.rightMargin; 79 | final int right = left + mDivider.getIntrinsicHeight(); 80 | mDivider.setBounds(left, top, right, bottom); 81 | mDivider.draw(c); 82 | } 83 | } 84 | 85 | @Override 86 | public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) { 87 | if (mOrientation == VERTICAL_LIST) { 88 | outRect.set(0, 0, 0, mDivider.getIntrinsicHeight()); 89 | } else { 90 | outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /app/src/main/java/com/fe/wdj/widget/SVRootLinearLayout.java: -------------------------------------------------------------------------------- 1 | package com.fe.wdj.widget; 2 | 3 | import android.animation.Animator; 4 | import android.animation.AnimatorListenerAdapter; 5 | import android.animation.ValueAnimator; 6 | import android.annotation.TargetApi; 7 | import android.content.Context; 8 | import android.os.Build; 9 | import android.util.AttributeSet; 10 | import android.view.MotionEvent; 11 | import android.view.ViewConfiguration; 12 | import android.widget.ImageView; 13 | import android.widget.LinearLayout; 14 | import android.widget.ScrollView; 15 | 16 | import com.fe.wdj.util.BigDecimalUtils; 17 | import com.fe.wdj.R; 18 | 19 | /** 20 | * Created by chenpengfei on 2016/11/22. 21 | */ 22 | public class SVRootLinearLayout extends LinearLayout { 23 | 24 | 25 | private LinearLayout mContentLL; 26 | private ImageView mIconImageView; 27 | private int mMargin; 28 | private boolean mIsAnimation, mIsLayoutImageView; 29 | private int mInitBottom; 30 | public int mContentMarginTop, mContentBottomOffset, mImageLeftOffset, mImageTopOffset, mTouchMoveOffset; 31 | 32 | 33 | private ScrollView mParentScrollView; 34 | private int mTitleViewHeight; 35 | private int mCenterVisibleViewHeight; 36 | private int mContentLlHeight, mContentLlWidth, mIconImageViewHeight, mIconImageViewWidth; 37 | 38 | /** 39 | * 拖拽 40 | */ 41 | private float mInitY; 42 | private int mTouchSlop; 43 | private boolean mIsDrag; 44 | private OnCloseListener mOnCloseListener; 45 | private OnUpdateBgColorListener mOnUpdateBgColorListener; 46 | 47 | 48 | public void setOnCloseListener(OnCloseListener onCloseListener) { 49 | mOnCloseListener = onCloseListener; 50 | } 51 | 52 | public void setOnUpdateBgColorListener(OnUpdateBgColorListener onUpdateBgColorListener) { 53 | mOnUpdateBgColorListener = onUpdateBgColorListener; 54 | } 55 | 56 | public SVRootLinearLayout(Context context) { 57 | super(context); 58 | } 59 | 60 | public SVRootLinearLayout(Context context, AttributeSet attrs) { 61 | super(context, attrs); 62 | mMargin = context.getResources().getDimensionPixelOffset(R.dimen.icon_margin); 63 | mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); 64 | } 65 | 66 | public SVRootLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) { 67 | super(context, attrs, defStyleAttr); 68 | } 69 | 70 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 71 | public SVRootLinearLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 72 | super(context, attrs, defStyleAttr, defStyleRes); 73 | } 74 | 75 | public int getCenterVisibleViewHeight() { 76 | return mCenterVisibleViewHeight; 77 | } 78 | 79 | public void setInitBottom(int bottom) { 80 | mInitBottom = bottom; 81 | } 82 | 83 | public void setAnimationStatus(boolean isAnimation) { 84 | mIsAnimation = isAnimation; 85 | } 86 | 87 | public void setLayoutImageView(boolean layoutImageView) { 88 | mIsLayoutImageView = layoutImageView; 89 | } 90 | 91 | @Override 92 | protected void onFinishInflate() { 93 | super.onFinishInflate(); 94 | mContentLL = (LinearLayout) findViewById(R.id.ll_content); 95 | mIconImageView = (ImageView) findViewById(R.id.imageview_icon); 96 | } 97 | 98 | @Override 99 | protected void onLayout(boolean changed, int l, int t, int r, int b) { 100 | super.onLayout(changed, l, t, r, b); 101 | int contentTop = mContentMarginTop + mTouchMoveOffset; 102 | mContentLL.layout(0, contentTop, mContentLlWidth, !mIsAnimation ? contentTop + mContentLlHeight : mInitBottom + mContentBottomOffset); 103 | 104 | if(!mIsLayoutImageView) return; 105 | int left = mMargin + mImageLeftOffset; 106 | int top = mMargin + mImageTopOffset; 107 | mIconImageView.layout(left, top, left + mIconImageViewWidth, top + mIconImageViewHeight); 108 | } 109 | 110 | @Override 111 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 112 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 113 | measureChildren(widthMeasureSpec, heightMeasureSpec); 114 | int childNumHeight = 0; 115 | for(int i = 0; i < getChildCount(); i++) { 116 | childNumHeight += getChildAt(i).getMeasuredHeight(); 117 | } 118 | if(childNumHeight > getMeasuredHeight()) { 119 | setMeasuredDimension(getMeasuredWidth(), childNumHeight); 120 | } 121 | 122 | mTitleViewHeight = getChildAt(0).getMeasuredHeight(); 123 | mContentLlHeight = mContentLL.getMeasuredHeight(); 124 | mContentLlWidth = mContentLL.getMeasuredWidth(); 125 | mIconImageViewHeight = mIconImageView.getMeasuredHeight(); 126 | mIconImageViewWidth = mIconImageView.getMeasuredWidth(); 127 | 128 | if(mParentScrollView == null) mParentScrollView = (ScrollView) getParent(); 129 | mCenterVisibleViewHeight = mParentScrollView.getHeight() - mTitleViewHeight; 130 | } 131 | 132 | public void setContentInitMarginTop(int marginTop) { 133 | mContentMarginTop = marginTop; 134 | requestLayout(); 135 | } 136 | 137 | public void setAllViewOffset(int contentMarginTop, int contentBottomOffset, int imageLeftOffset, int imageTopOffset) { 138 | mContentMarginTop = contentMarginTop; 139 | mContentBottomOffset = contentBottomOffset; 140 | mImageLeftOffset = imageLeftOffset; 141 | mImageTopOffset = imageTopOffset; 142 | requestLayout(); 143 | } 144 | 145 | public void setTouchMoveOffset(float touchMoveOffset) { 146 | if(touchMoveOffset < 0) touchMoveOffset = 0; 147 | mTouchMoveOffset = (int) touchMoveOffset; 148 | requestLayout(); 149 | updateBgColor(mTouchMoveOffset); 150 | } 151 | 152 | public void updateBgColor(int offset) { 153 | if(mOnUpdateBgColorListener != null) { 154 | float ratio = BigDecimalUtils.divide(offset, mCenterVisibleViewHeight); 155 | if(ratio > 1) ratio = 1; 156 | if(ratio < 0) ratio = 0; 157 | mOnUpdateBgColorListener.onUpdate(ratio); 158 | } 159 | } 160 | 161 | @Override 162 | public boolean onInterceptTouchEvent(MotionEvent event) { 163 | 164 | return true; 165 | } 166 | 167 | @Override 168 | public boolean onTouchEvent(MotionEvent event) { 169 | boolean consumption = true; 170 | switch (event.getAction()) { 171 | case MotionEvent.ACTION_DOWN: 172 | mInitY = event.getY(); 173 | getParent().requestDisallowInterceptTouchEvent(true); 174 | break; 175 | case MotionEvent.ACTION_MOVE: 176 | float moveY = event.getY(); 177 | float yOffset = moveY - mInitY; 178 | //拖动 179 | if((mParentScrollView.getScrollY() <= 0 && moveY >= mInitY) || mIsDrag) { 180 | setTouchMoveOffset(yOffset); 181 | mIsDrag = true; 182 | consumption = true; 183 | } else { 184 | getParent().requestDisallowInterceptTouchEvent(false); 185 | consumption = false; 186 | } 187 | break; 188 | case MotionEvent.ACTION_UP: 189 | mIsDrag = false; 190 | boolean isUp = false; 191 | int animationMoveOffset; 192 | if(mContentLL.getTop() <= mCenterVisibleViewHeight / 2 + mTitleViewHeight) { 193 | animationMoveOffset = mTouchMoveOffset; 194 | isUp = true; 195 | } else { 196 | animationMoveOffset = mParentScrollView.getHeight() - mContentLL.getTop(); 197 | } 198 | startAnimation(animationMoveOffset, isUp, mTouchMoveOffset); 199 | break; 200 | } 201 | return consumption; 202 | } 203 | 204 | public void startAnimation(final int moveOffset, final boolean isUp, final int currentMoveOffset) { 205 | int duration = moveOffset / mTouchSlop * 10; 206 | if(duration <= 0) duration = 300; 207 | ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1).setDuration(duration); 208 | valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 209 | @Override 210 | public void onAnimationUpdate(ValueAnimator animation) { 211 | float ratio = (float) animation.getAnimatedValue(); 212 | if(isUp) 213 | mTouchMoveOffset = (int) (moveOffset * (1 - ratio)); 214 | else 215 | mTouchMoveOffset = currentMoveOffset + (int) (moveOffset * ratio); 216 | requestLayout(); 217 | updateBgColor(mTouchMoveOffset); 218 | } 219 | }); 220 | 221 | valueAnimator.addListener(new AnimatorListenerAdapter() { 222 | @Override 223 | public void onAnimationEnd(Animator animation) { 224 | super.onAnimationEnd(animation); 225 | if(!isUp && mOnCloseListener != null) mOnCloseListener.onClose(); 226 | } 227 | }); 228 | valueAnimator.start(); 229 | } 230 | 231 | public interface OnCloseListener { 232 | void onClose(); 233 | } 234 | 235 | public interface OnUpdateBgColorListener { 236 | void onUpdate(float ratio); 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /app/src/main/res/anim/activity_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/hg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenpengfei88/WdjAppDetail/055d6562642d04859f2cb4b004f4c64d1e85607e/app/src/main/res/drawable-xxhdpi/hg.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_aqy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenpengfei88/WdjAppDetail/055d6562642d04859f2cb4b004f4c64d1e85607e/app/src/main/res/drawable-xxhdpi/ic_aqy.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_bb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenpengfei88/WdjAppDetail/055d6562642d04859f2cb4b004f4c64d1e85607e/app/src/main/res/drawable-xxhdpi/ic_bb.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_cz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenpengfei88/WdjAppDetail/055d6562642d04859f2cb4b004f4c64d1e85607e/app/src/main/res/drawable-xxhdpi/ic_cz.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_kk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenpengfei88/WdjAppDetail/055d6562642d04859f2cb4b004f4c64d1e85607e/app/src/main/res/drawable-xxhdpi/ic_kk.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_kr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenpengfei88/WdjAppDetail/055d6562642d04859f2cb4b004f4c64d1e85607e/app/src/main/res/drawable-xxhdpi/ic_kr.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_qq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenpengfei88/WdjAppDetail/055d6562642d04859f2cb4b004f4c64d1e85607e/app/src/main/res/drawable-xxhdpi/ic_qq.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_sg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenpengfei88/WdjAppDetail/055d6562642d04859f2cb4b004f4c64d1e85607e/app/src/main/res/drawable-xxhdpi/ic_sg.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_xl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenpengfei88/WdjAppDetail/055d6562642d04859f2cb4b004f4c64d1e85607e/app/src/main/res/drawable-xxhdpi/ic_xl.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_yk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenpengfei88/WdjAppDetail/055d6562642d04859f2cb4b004f4c64d1e85607e/app/src/main/res/drawable-xxhdpi/ic_yk.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_yyy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenpengfei88/WdjAppDetail/055d6562642d04859f2cb4b004f4c64d1e85607e/app/src/main/res/drawable-xxhdpi/ic_yyy.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/mv.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenpengfei88/WdjAppDetail/055d6562642d04859f2cb4b004f4c64d1e85607e/app/src/main/res/drawable-xxhdpi/mv.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/xg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenpengfei88/WdjAppDetail/055d6562642d04859f2cb4b004f4c64d1e85607e/app/src/main/res/drawable-xxhdpi/xg.gif -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/zy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenpengfei88/WdjAppDetail/055d6562642d04859f2cb4b004f4c64d1e85607e/app/src/main/res/drawable-xxhdpi/zy.jpg -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_app_detail.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_detail.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 16 | 17 | 23 | 24 | 28 | 29 | 35 | 36 | 37 | 38 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_detail_bottom.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 17 | 18 | 26 | 27 | 35 | 36 |