Repository: huage2580/ZuiMeiTAG Branch: master Commit: 2a5005b406b6 Files: 34 Total size: 38.3 KB Directory structure: gitextract_zm55re4_/ ├── .gitignore ├── .idea/ │ ├── .name │ ├── compiler.xml │ ├── copyright/ │ │ └── profiles_settings.xml │ ├── encodings.xml │ ├── gradle.xml │ ├── misc.xml │ ├── modules.xml │ └── runConfigurations.xml ├── README.md ├── app/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── com/ │ │ └── toxicant/ │ │ └── hua/ │ │ └── zuimeitag/ │ │ └── ApplicationTest.java │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── java/ │ │ │ └── com/ │ │ │ └── toxicant/ │ │ │ └── hua/ │ │ │ └── zuimeitag/ │ │ │ ├── MainActivity.java │ │ │ ├── MyLayoutManager.java │ │ │ └── TAGRecyclerView.java │ │ └── res/ │ │ ├── drawable/ │ │ │ └── bg_white.xml │ │ ├── layout/ │ │ │ ├── activity_main.xml │ │ │ └── item.xml │ │ ├── values/ │ │ │ ├── colors.xml │ │ │ ├── dimens.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ └── values-w820dp/ │ │ └── dimens.xml │ └── test/ │ └── java/ │ └── com/ │ └── toxicant/ │ └── hua/ │ └── zuimeitag/ │ └── ExampleUnitTest.java ├── build.gradle ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat └── settings.gradle ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ *.iml .gradle /local.properties /.idea/workspace.xml /.idea/libraries .DS_Store /build /captures ================================================ FILE: .idea/.name ================================================ ZuiMeiTAG ================================================ FILE: .idea/compiler.xml ================================================ ================================================ FILE: .idea/copyright/profiles_settings.xml ================================================ ================================================ FILE: .idea/encodings.xml ================================================ ================================================ FILE: .idea/gradle.xml ================================================ ================================================ FILE: .idea/misc.xml ================================================ ================================================ FILE: .idea/modules.xml ================================================ ================================================ FILE: .idea/runConfigurations.xml ================================================ ================================================ FILE: README.md ================================================ # ZuiMeiTAG 仿最美应用的底栏,使用recyclerView实现,相关博文http://www.jianshu.com/p/7202416974f6 能不能给个star鼓励下(●'◡'●) #效果图 ![效果图](https://github.com/huage2580/ZuiMeiTAG/raw/master/hello.gif) ================================================ FILE: app/.gitignore ================================================ /build ================================================ FILE: app/build.gradle ================================================ apply plugin: 'com.android.application' android { compileSdkVersion 23 buildToolsVersion "23.0.2" defaultConfig { applicationId "com.toxicant.hua.zuimeitag" minSdkVersion 16 targetSdkVersion 23 versionCode 1 versionName "1.0" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:23.4.0' compile 'com.android.support:recyclerview-v7:23.4.0' } ================================================ FILE: app/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # By default, the flags in this file are appended to flags specified # in E:\AndroidStudio\SDK/tools/proguard/proguard-android.txt # You can edit the include path and order by changing the proguardFiles # directive in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # Add any project specific keep options here: # If your project uses WebView with JS, uncomment the following # and specify the fully qualified class name to the JavaScript interface # class: #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} ================================================ FILE: app/src/androidTest/java/com/toxicant/hua/zuimeitag/ApplicationTest.java ================================================ package com.toxicant.hua.zuimeitag; import android.app.Application; import android.test.ApplicationTestCase; /** * Testing Fundamentals */ public class ApplicationTest extends ApplicationTestCase { public ApplicationTest() { super(Application.class); } } ================================================ FILE: app/src/main/AndroidManifest.xml ================================================ ================================================ FILE: app/src/main/java/com/toxicant/hua/zuimeitag/MainActivity.java ================================================ package com.toxicant.hua.zuimeitag; import android.animation.ObjectAnimator; import android.graphics.Bitmap; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.animation.Animation; import android.view.animation.TranslateAnimation; import android.widget.Button; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; import java.util.ArrayList; public class MainActivity extends AppCompatActivity { TAGRecyclerView rv; Button btn1; Button btn2; ArrayList datas; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btn1= (Button) findViewById(R.id.button); btn2= (Button) findViewById(R.id.button2); rv= (TAGRecyclerView) findViewById(R.id.rv1); rv.setLayoutManager(new MyLayoutManager()); datas=new ArrayList<>(); for (int i=0;i<20;i++){ datas.add("item==>"+i); } // final int temp=height/7; final RvAdapter adapter = new RvAdapter(); rv.setAdapter(adapter); btn1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { rv.selectNextItem(); } }); btn2.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { rv.selectPreviousItem(); } }); rv.setSwitchListener(new TAGRecyclerView.SwitchListener() { @Override public void onSwitch(int realIndex) { Toast.makeText(MainActivity.this,"当前选择=>"+realIndex,Toast.LENGTH_SHORT).show(); } }); } class RvAdapter extends RecyclerView.Adapter{ @Override public ViewHoler onCreateViewHolder(ViewGroup parent, int viewType) { return new ViewHoler(LayoutInflater.from(parent.getContext()).inflate(R.layout.item,null,false)); } @Override public void onBindViewHolder(final ViewHoler holder, int position) { holder.tv1.setText(datas.get(position)); } @Override public int getItemCount() { return datas.size(); } } class ViewHoler extends RecyclerView.ViewHolder{ ImageView ivl; TextView tv1; LinearLayout ll; public ViewHoler(View itemView) { super(itemView); ivl= (ImageView) itemView.findViewById(R.id.iv); tv1= (TextView) itemView.findViewById(R.id.tv1); ll= (LinearLayout) itemView.findViewById(R.id.linear_layout); itemView.setClickable(false); } } } ================================================ FILE: app/src/main/java/com/toxicant/hua/zuimeitag/MyLayoutManager.java ================================================ package com.toxicant.hua.zuimeitag; import android.graphics.PointF; import android.graphics.Rect; import android.support.v7.widget.LinearSmoothScroller; import android.support.v7.widget.RecyclerView; import android.util.SparseArray; import android.util.SparseBooleanArray; import android.view.View; import android.view.ViewGroup; /** * Created by hua on 2016.12.10. */ public class MyLayoutManager extends RecyclerView.LayoutManager { public int mSelectIndex=0; final int mPadding=3; int allWidth=0; int offsetScroll=0; int firstView=0;//第一个可视view序号 boolean firstLayout=true;//每次开始布局 //保存所有的Item的上下左右的偏移量信息 private SparseArray allItemFrames = new SparseArray<>(); //记录Item是否出现过屏幕且还没有回收。true表示出现过屏幕上,并且还没被回收 private SparseBooleanArray hasAttachedItems = new SparseBooleanArray(); @Override public RecyclerView.LayoutParams generateDefaultLayoutParams() { return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); } @Override public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { detachAndScrapAttachedViews(recycler); int offsetX=mPadding; for(int i=0;iallWidth-getWidth()){//最右边 travel=allWidth-getWidth()-offsetScroll; } offsetScroll+=travel; offsetChildrenHorizontal(-travel); recyclerAndFillItems(recycler, state); return travel; } void recyclerAndFillItems(RecyclerView.Recycler recycler, RecyclerView.State state){ if (state.isPreLayout()) { // 跳过preLayout,preLayout主要用于支持动画 return; } // 当前scroll offset状态下的显示区域 Rect displayFrame = new Rect(offsetScroll,0, getWidth()+offsetScroll, getHeight()); // Log.e("display","left->"+displayFrame.left+"right->"+displayFrame.right); //回收 Rect childFrame = new Rect(); for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); childFrame.left = getDecoratedLeft(child); childFrame.top = getDecoratedTop(child); childFrame.right = getDecoratedRight(child); childFrame.bottom = getDecoratedBottom(child); // Log.e("回收判断","left->"+childFrame.left+"right->"+childFrame.right); //如果Item没有在显示区域,就说明需要回收 if (!Rect.intersects(displayFrame, childFrame)) { //回收掉滑出屏幕的View removeAndRecycleView(child, recycler); } } //显示 firstLayout=true; for (int i = 0; i < getItemCount(); i++) { if (Rect.intersects(displayFrame, allItemFrames.get(i))) { // Log.e("layout","显示==>"+i); if(firstLayout){ firstView=i; firstLayout=false; } View scrap = recycler.getViewForPosition(i); measureChildWithMargins(scrap, 0, 0); int height = getDecoratedMeasuredHeight(scrap); if (i!=mSelectIndex){ scrap.setTranslationY(height/7*6); } addView(scrap); Rect frame = allItemFrames.get(i); // Log.e("显示判断","left->"+frame.left+"right->"+frame.right); //将这个item布局出来 layoutDecorated(scrap, frame.left-offsetScroll, frame.top, frame.right-offsetScroll, frame.bottom); } } } } ================================================ FILE: app/src/main/java/com/toxicant/hua/zuimeitag/TAGRecyclerView.java ================================================ package com.toxicant.hua.zuimeitag; import android.animation.Animator; import android.animation.ObjectAnimator; import android.content.Context; import android.support.annotation.Nullable; import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; /** * Created by hua on 2016.12.11. */ public class TAGRecyclerView extends RecyclerView implements View.OnTouchListener { private int mOffsetIndex;//偏移量,显示的第一个view的序号 [0,n) private int mSelectIndex;//当前选择的真实序号 [0,n) private SwitchListener mSwitchListener; interface SwitchListener{ void onSwitch(int realIndex); } public TAGRecyclerView(Context context) { super(context); init(); } public TAGRecyclerView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(); } public TAGRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } void init(){ mOffsetIndex=0; mSelectIndex=0; setOnTouchListener(this); } @Override public boolean onTouch(View v, MotionEvent event) { int action = event.getAction(); float x=event.getX(); final int index= (int) (x/(this.getWidth()/7)); //Log.i("index","index->"+index); switch (action){ case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_MOVE: final int th=this.getHeight()/7; //view突出 for (int i=0;i<7;i++){ int t=Math.abs(index-i); // Log.i("突出","点击->"+index+"当前相距->"+t+"第一个view:"+count); this.getChildAt(i).setTranslationY(t*th); } break; case MotionEvent.ACTION_UP: //记录位置 RecyclerView.LayoutManager lm= this.getLayoutManager(); if (lm instanceof MyLayoutManager){ mOffsetIndex = ((MyLayoutManager) lm).firstView; mSelectIndex=mOffsetIndex+index; ((MyLayoutManager) lm).mSelectIndex=mSelectIndex; //Log.i("up","抬起相对位置-"+index+"偏移量-"+mOffsetIndex+"选择的真实序号-"+mSelectIndex); } if (mSwitchListener!=null){ mSwitchListener.onSwitch(mSelectIndex); } boolean isFirstAnim=true; //下落动画 for (int i=0;i<7;i++){ if (i==index){ continue; } View temp=this.getChildAt(i); ObjectAnimator anim=ObjectAnimator// .ofFloat(temp, "TranslationY", temp.getTranslationY(),this.getHeight()/7*6)// .setDuration(500); anim.start(); if (isFirstAnim){ anim.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { //位移动画 scrollItem(index); } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); isFirstAnim=false; } } break; } return true; } /** * 把这个item放到中间,是相对序号 * @param index 对应屏幕上的第几个[0,n) */ private void scrollItem(int index){ int dx=0; final int tw=TAGRecyclerView.this.getWidth()/7; if (index<3){ dx=-tw*(3-index); }else if(index>3){ dx=tw*(index-3); } TAGRecyclerView.this.smoothScrollBy(dx,0); } /** * 切换到下一个item */ public boolean selectNextItem(){ if (mSelectIndex>=TAGRecyclerView.this.getAdapter().getItemCount()-1){//已经是最后一个 return false; } //当前选择的item下落 final RecyclerView.LayoutManager lm= this.getLayoutManager(); if (lm instanceof MyLayoutManager) { mOffsetIndex = ((MyLayoutManager) lm).firstView; View view=this.getChildAt(mSelectIndex-mOffsetIndex); final ObjectAnimator anim=ObjectAnimator.ofFloat(view, "TranslationY", view.getTranslationY(),this.getHeight()/7*6) .setDuration(200); anim.start(); anim.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { //下一个item升起 mSelectIndex+=1; ((MyLayoutManager) lm).mSelectIndex=mSelectIndex; View view=TAGRecyclerView.this.getChildAt(mSelectIndex-mOffsetIndex); ObjectAnimator animR=ObjectAnimator.ofFloat(view, "TranslationY", view.getTranslationY(),0) .setDuration(200); animR.start(); animR.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { scrollItem(mSelectIndex-mOffsetIndex); } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); } return true; } /** * 切换到上一个item */ public boolean selectPreviousItem(){ if (mSelectIndex<1){//已经是第一个 return false; } //当前选择的item下落 final RecyclerView.LayoutManager lm= this.getLayoutManager(); if (lm instanceof MyLayoutManager) { mOffsetIndex = ((MyLayoutManager) lm).firstView; View view=this.getChildAt(mSelectIndex-mOffsetIndex); final ObjectAnimator anim=ObjectAnimator.ofFloat(view, "TranslationY", view.getTranslationY(),this.getHeight()/7*6) .setDuration(200); anim.start(); anim.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { //上一个item升起 mSelectIndex-=1; ((MyLayoutManager) lm).mSelectIndex=mSelectIndex; View view=TAGRecyclerView.this.getChildAt(mSelectIndex-mOffsetIndex); ObjectAnimator animR=ObjectAnimator.ofFloat(view, "TranslationY", view.getTranslationY(),0) .setDuration(200); animR.start(); animR.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { scrollItem(mSelectIndex-mOffsetIndex); } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); } return true; } /** * 当前选择的item真实序号 * @return */ public int getSelectIndex(){ return mSelectIndex; } public void setSwitchListener(SwitchListener listener){ this.mSwitchListener=listener; } } ================================================ FILE: app/src/main/res/drawable/bg_white.xml ================================================ ================================================ FILE: app/src/main/res/layout/activity_main.xml ================================================