├── .gitignore ├── .idea ├── gradle.xml ├── kotlinc.xml ├── misc.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── orient │ │ └── test │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── orient │ │ │ └── test │ │ │ ├── MainActivity.java │ │ │ ├── adapter │ │ │ ├── BookAdapter.java │ │ │ ├── GlideAdapter.java │ │ │ ├── GridPhotoAdapter.java │ │ │ ├── MainAdapter.java │ │ │ └── PhotoAdapter.java │ │ │ ├── animation │ │ │ ├── ContentScaleAnimation.java │ │ │ └── Rotate3DAnimation.java │ │ │ ├── common │ │ │ └── Image.java │ │ │ ├── ui │ │ │ └── activity │ │ │ │ ├── BookSampleActivity.java │ │ │ │ ├── ConstraintActivity.java │ │ │ │ ├── NetWorkActivity.java │ │ │ │ └── OpenBookActivity.java │ │ │ └── utils │ │ │ ├── GlideUtils.java │ │ │ └── UiUtils.java │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── beauty.jpeg │ │ ├── ic_constraint_add.xml │ │ ├── ic_constraint_like.xml │ │ ├── ic_constraint_top.xml │ │ ├── ic_constraint_write.xml │ │ ├── ic_launcher_background.xml │ │ ├── preview.jpg │ │ ├── shape_bottom_menu_green.xml │ │ ├── shape_item_empty.xml │ │ ├── shape_main_item.xml │ │ ├── th.jpg │ │ └── theme_leather_bg.jpg │ │ ├── layout │ │ ├── activity_book_sample.xml │ │ ├── activity_constraint.xml │ │ ├── activity_main.xml │ │ ├── activity_net_work.xml │ │ ├── activity_open_book.xml │ │ ├── recycle_item_main.xml │ │ ├── recycle_item_net_work.xml │ │ └── recycler_item_book.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── orient │ └── test │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── pic └── title.jpg └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/libraries 5 | /.idea/modules.xml 6 | /.idea/workspace.xml 7 | .DS_Store 8 | /build 9 | /captures 10 | .externalNativeBuild 11 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | Abstraction issuesJava 36 | 37 | 38 | Android 39 | 40 | 41 | Assignment issuesJava 42 | 43 | 44 | Bitwise operation issuesJava 45 | 46 | 47 | Class metricsJava 48 | 49 | 50 | Class structureJava 51 | 52 | 53 | Cloning issuesJava 54 | 55 | 56 | Code maturity issuesJava 57 | 58 | 59 | Code style issuesJava 60 | 61 | 62 | Compiler issuesJava 63 | 64 | 65 | Concurrency annotation issuesJava 66 | 67 | 68 | Control flow issuesJava 69 | 70 | 71 | CorrectnessLintAndroid 72 | 73 | 74 | Data flow issuesJava 75 | 76 | 77 | Declaration redundancyJava 78 | 79 | 80 | Dependency issuesJava 81 | 82 | 83 | Encapsulation issuesJava 84 | 85 | 86 | Error handlingJava 87 | 88 | 89 | Finalization issuesJava 90 | 91 | 92 | GPath inspectionsGroovy 93 | 94 | 95 | General 96 | 97 | 98 | GeneralJava 99 | 100 | 101 | Google Cloud Endpoints 102 | 103 | 104 | Groovy 105 | 106 | 107 | ImportsJava 108 | 109 | 110 | Inheritance issuesJava 111 | 112 | 113 | Initialization issuesJava 114 | 115 | 116 | Internationalization issuesJava 117 | 118 | 119 | J2ME issuesJava 120 | 121 | 122 | JUnit issuesJava 123 | 124 | 125 | Java 126 | 127 | 128 | Java 5Java language level migration aidsJava 129 | 130 | 131 | Java 7Java language level migration aidsJava 132 | 133 | 134 | Java 8Java language level migration aidsJava 135 | 136 | 137 | Java interop issuesKotlin 138 | 139 | 140 | Java language level issuesJava 141 | 142 | 143 | Java language level migration aidsJava 144 | 145 | 146 | JavaBeans issuesJava 147 | 148 | 149 | Javadoc issuesJava 150 | 151 | 152 | Kotlin 153 | 154 | 155 | Language Injection 156 | 157 | 158 | LintAndroid 159 | 160 | 161 | Logging issuesJava 162 | 163 | 164 | Manifest 165 | 166 | 167 | Memory issuesJava 168 | 169 | 170 | MessagesCorrectnessLintAndroid 171 | 172 | 173 | Method metricsJava 174 | 175 | 176 | Modularization issuesJava 177 | 178 | 179 | Naming ConventionsGroovy 180 | 181 | 182 | Naming conventionsJava 183 | 184 | 185 | Numeric issuesJava 186 | 187 | 188 | Packaging issuesJava 189 | 190 | 191 | Performance issuesJava 192 | 193 | 194 | PerformanceLintAndroid 195 | 196 | 197 | Portability issuesJava 198 | 199 | 200 | Potentially confusing code constructsGroovy 201 | 202 | 203 | Probable bugsGroovy 204 | 205 | 206 | Probable bugsJava 207 | 208 | 209 | Probable bugsKotlin 210 | 211 | 212 | Properties Files 213 | 214 | 215 | Properties FilesJava 216 | 217 | 218 | Resource management issuesJava 219 | 220 | 221 | Security issuesJava 222 | 223 | 224 | SecurityLintAndroid 225 | 226 | 227 | Serialization issuesJava 228 | 229 | 230 | Style issuesKotlin 231 | 232 | 233 | StyleGroovy 234 | 235 | 236 | TestNGJava 237 | 238 | 239 | Threading issuesGroovy 240 | 241 | 242 | Threading issuesJava 243 | 244 | 245 | UsabilityLintAndroid 246 | 247 | 248 | Verbose or redundant code constructsJava 249 | 250 | 251 | Visibility issuesJava 252 | 253 | 254 | toString() issuesJava 255 | 256 | 257 | 258 | 259 | Android 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 271 | 272 | 273 | 274 | 275 | 1.8 276 | 277 | 282 | 283 | 284 | 285 | 286 | 287 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![标题](https://github.com/mCyp/Test/blob/master/pic/title.jpg) 2 | 3 | # Test 4 | 5 | #### ⭐️ 来都来了,不star一下吗?关于安卓的一些小知识,本人也会不定期更新中~ 6 | 7 | 8 | 9 | ### 一. 书籍打开动画 10 | 11 | > 简书: [仿掌阅实现书籍打开动画](https://www.jianshu.com/p/8a031a945b79) 12 | 13 | ### 二. LruCache和DiskLruCache 14 | 15 | > 简书:[带你学习Android图片缓存机制](https://www.jianshu.com/p/0aef0779d813) 16 | 17 | ### 三. ConstraintLayout中Circular positioning 18 | 19 | > 简书:[妙用ConstraintLayout的Circular positioning](https://www.jianshu.com/p/7f111f0bdbd0) 20 | 21 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 28 5 | defaultConfig { 6 | applicationId "com.orient.test" 7 | minSdkVersion 21 8 | targetSdkVersion 28 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | implementation fileTree(dir: 'libs', include: ['*.jar']) 23 | implementation 'com.android.support:appcompat-v7:28.0.0' 24 | implementation 'com.android.support.constraint:constraint-layout:1.1.3' 25 | testImplementation 'junit:junit:4.12' 26 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 27 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 28 | 29 | implementation("com.squareup.okhttp3:okhttp:3.12.1") 30 | 31 | // RecyclerView 32 | implementation 'com.android.support:recyclerview-v7:28.0.0' 33 | // 卡片布局 34 | implementation "com.android.support:cardview-v7:28.0.0" 35 | implementation 'com.android.support:design:28.0.0' 36 | 37 | implementation 'com.jakewharton:disklrucache:2.0.2' 38 | implementation 'com.github.bumptech.glide:glide:3.7.0' 39 | 40 | 41 | } 42 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/orient/test/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.orient.test; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.orient.test", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/orient/test/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.orient.test; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | import android.support.v7.widget.LinearLayoutManager; 6 | import android.support.v7.widget.RecyclerView; 7 | 8 | import com.orient.test.adapter.MainAdapter; 9 | import com.orient.test.ui.activity.ConstraintActivity; 10 | import com.orient.test.ui.activity.NetWorkActivity; 11 | import com.orient.test.ui.activity.OpenBookActivity; 12 | 13 | public class MainActivity extends AppCompatActivity implements MainAdapter.OnSelectListener{ 14 | private static final String TAG = "MainActivity"; 15 | 16 | private RecyclerView mRecyclerView; 17 | private MainAdapter mAdapter; 18 | 19 | @Override 20 | protected void onCreate(Bundle savedInstanceState) { 21 | super.onCreate(savedInstanceState); 22 | setContentView(R.layout.activity_main); 23 | 24 | initWidget(); 25 | } 26 | 27 | private void initWidget() { 28 | mRecyclerView = findViewById(R.id.recycle); 29 | mAdapter = new MainAdapter(this); 30 | mRecyclerView.setLayoutManager(new LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false)); 31 | mRecyclerView.setAdapter(mAdapter); 32 | } 33 | 34 | @Override 35 | public void onSelectStr(String str) { 36 | switch (str){ 37 | case "书籍打开动画": 38 | OpenBookActivity.show(this); 39 | break; 40 | case "LruCache和DiskLruCache": 41 | NetWorkActivity.show(this); 42 | break; 43 | case "ConstraintLayout中Circular positioning": 44 | ConstraintActivity.show(this); 45 | break; 46 | default: 47 | break; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/java/com/orient/test/adapter/BookAdapter.java: -------------------------------------------------------------------------------- 1 | package com.orient.test.adapter; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.graphics.BitmapFactory; 6 | import android.support.annotation.DrawableRes; 7 | import android.support.annotation.NonNull; 8 | import android.support.v7.widget.RecyclerView; 9 | import android.view.LayoutInflater; 10 | import android.view.View; 11 | import android.view.ViewGroup; 12 | import android.widget.ImageView; 13 | import android.widget.TextView; 14 | 15 | import com.orient.test.R; 16 | 17 | import java.util.ArrayList; 18 | import java.util.List; 19 | 20 | /** 21 | * Author WangJie 22 | * Created on 2019/1/21. 23 | */ 24 | public class BookAdapter extends RecyclerView.Adapter { 25 | 26 | private List values; 27 | private OnBookClickListener listener; 28 | 29 | public BookAdapter(List values,OnBookClickListener listener) { 30 | this.values = values; 31 | this.listener = listener; 32 | } 33 | 34 | @NonNull 35 | @Override 36 | public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) { 37 | View root = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.recycler_item_book,viewGroup,false); 38 | ViewHolder viewHolder = new ViewHolder(root); 39 | viewHolder.cover = root.findViewById(R.id.preview); 40 | viewHolder.mTitle = root.findViewById(R.id.txt_name); 41 | return viewHolder; 42 | } 43 | 44 | @Override 45 | public void onBindViewHolder(@NonNull final ViewHolder viewHolder,int i) { 46 | viewHolder.cover.setImageResource(R.drawable.preview); 47 | viewHolder.mTitle.setText("平凡的世界"); 48 | 49 | viewHolder.cover.setOnClickListener(new View.OnClickListener() { 50 | @Override 51 | public void onClick(View v) { 52 | listener.onItemClick(viewHolder.getAdapterPosition(),viewHolder.cover); 53 | } 54 | }); 55 | } 56 | 57 | @Override 58 | public int getItemCount() { 59 | return values.size(); 60 | } 61 | 62 | public class ViewHolder extends RecyclerView.ViewHolder{ 63 | ImageView cover; 64 | TextView mTitle; 65 | 66 | public ViewHolder(@NonNull View itemView) { 67 | super(itemView); 68 | } 69 | } 70 | 71 | public interface OnBookClickListener{ 72 | void onItemClick(int pos,View view); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /app/src/main/java/com/orient/test/adapter/GlideAdapter.java: -------------------------------------------------------------------------------- 1 | package com.orient.test.adapter; 2 | 3 | import android.content.Context; 4 | import android.support.annotation.NonNull; 5 | import android.support.v7.widget.RecyclerView; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.ImageView; 10 | 11 | import com.orient.test.R; 12 | import com.orient.test.utils.GlideUtils; 13 | 14 | /** 15 | * Created by wangjie on 2019/2/12. 16 | */ 17 | 18 | public class GlideAdapter extends RecyclerView.Adapter{ 19 | 20 | // 照片的网络路径 21 | private String[] urls; 22 | private Context mContext; 23 | 24 | public GlideAdapter(String[] urls, Context context) { 25 | this.urls = urls; 26 | this.mContext = context; 27 | } 28 | 29 | @NonNull 30 | @Override 31 | public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) { 32 | View root = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.recycle_item_net_work,viewGroup,false); 33 | ViewHolder viewHolder = new ViewHolder(root); 34 | viewHolder.imageView = root.findViewById(R.id.grid_photo); 35 | return viewHolder; 36 | } 37 | 38 | @Override 39 | public void onBindViewHolder(@NonNull ViewHolder viewHolder, int i) { 40 | ImageView imageView = viewHolder.imageView; 41 | String url = urls[i]; 42 | GlideUtils.loadUrl(mContext,url,imageView); 43 | } 44 | 45 | @Override 46 | public int getItemCount() { 47 | return urls.length; 48 | } 49 | 50 | public class ViewHolder extends RecyclerView.ViewHolder{ 51 | 52 | public ImageView imageView; 53 | 54 | public ViewHolder(@NonNull View itemView) { 55 | super(itemView); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /app/src/main/java/com/orient/test/adapter/GridPhotoAdapter.java: -------------------------------------------------------------------------------- 1 | package com.orient.test.adapter; 2 | 3 | import android.content.Context; 4 | import android.content.pm.PackageInfo; 5 | import android.content.pm.PackageManager; 6 | import android.graphics.Bitmap; 7 | import android.graphics.BitmapFactory; 8 | import android.os.Environment; 9 | import android.os.Handler; 10 | import android.support.annotation.NonNull; 11 | import android.support.v4.util.LruCache; 12 | import android.support.v7.widget.RecyclerView; 13 | import android.view.LayoutInflater; 14 | import android.view.View; 15 | import android.view.ViewGroup; 16 | import android.widget.ImageView; 17 | 18 | import com.jakewharton.disklrucache.DiskLruCache; 19 | import com.orient.test.R; 20 | 21 | import java.io.BufferedInputStream; 22 | import java.io.BufferedOutputStream; 23 | import java.io.File; 24 | import java.io.FileDescriptor; 25 | import java.io.FileInputStream; 26 | import java.io.IOException; 27 | import java.io.OutputStream; 28 | import java.security.MessageDigest; 29 | import java.security.NoSuchAlgorithmException; 30 | import java.util.concurrent.ExecutorService; 31 | import java.util.concurrent.Executors; 32 | 33 | import okhttp3.Call; 34 | import okhttp3.OkHttpClient; 35 | import okhttp3.Request; 36 | import okhttp3.Response; 37 | 38 | /** 39 | * 瀑布适配器 40 | * 41 | * Created by wangjie on 2019/1/30. 42 | */ 43 | 44 | public class GridPhotoAdapter extends RecyclerView.Adapter { 45 | 46 | public static final String TAG = "GridPhotoAdapter"; 47 | // 照片的网络路径 48 | private String[] urls; 49 | // 内存缓存 50 | private LruCache mMemoryCache; 51 | // 硬盘缓存 52 | private DiskLruCache mDisLruCache; 53 | // OkhttpClient 54 | private OkHttpClient okHttpClient; 55 | // 线程池 用来请求下载图片 56 | private ExecutorService service; 57 | // 主线程Handler 用来图片下载完成后更新ImageView 58 | private Handler mHandler; 59 | // 上下文 60 | private Context mContext; 61 | 62 | public GridPhotoAdapter(String[] urls, Handler mHandler, Context context) { 63 | this.urls = urls; 64 | this.mHandler = mHandler; 65 | this.mContext = context; 66 | init(); 67 | } 68 | 69 | /* 70 | 一些必要的初始化的工作 71 | */ 72 | private void init() { 73 | okHttpClient = new OkHttpClient.Builder() 74 | .build(); 75 | 76 | // 构建一定数量的线程池 77 | service = Executors.newFixedThreadPool(6); 78 | 79 | // 构建内存缓存 80 | int maxMemory = (int) Runtime.getRuntime().maxMemory(); 81 | int cacheSize = maxMemory/8; 82 | mMemoryCache = new LruCache(cacheSize){ 83 | @Override 84 | protected int sizeOf(@NonNull String key, @NonNull Bitmap value) { 85 | return value.getByteCount(); 86 | } 87 | }; 88 | 89 | // 构建硬盘缓存实例 90 | File file = getDiskCacheDir(mContext,"photo"); 91 | if(!file.exists()) 92 | file.mkdirs(); 93 | try { 94 | mDisLruCache = DiskLruCache.open(file,getAppInfoVersion(),1,10*1024*1024); 95 | } catch (IOException e) { 96 | e.printStackTrace(); 97 | } 98 | } 99 | 100 | /* 101 | 根据传入的uniqueName获取唯一的硬盘的缓存路径 102 | */ 103 | private File getDiskCacheDir(Context context, String uniqueName) { 104 | String cachePath; 105 | if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) 106 | || !Environment.isExternalStorageRemovable()) { 107 | cachePath = context.getExternalCacheDir().getPath(); 108 | } else { 109 | cachePath = context.getCacheDir().getPath(); 110 | } 111 | return new File(cachePath + File.separator + uniqueName); 112 | } 113 | 114 | private int getAppInfoVersion() { 115 | try { 116 | PackageInfo info = mContext.getPackageManager().getPackageInfo(mContext.getPackageName(), 0); 117 | return info.versionCode; 118 | } catch (PackageManager.NameNotFoundException e) { 119 | e.printStackTrace(); 120 | } 121 | return 1; 122 | } 123 | 124 | @NonNull 125 | @Override 126 | public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) { 127 | View root = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.recycle_item_net_work,viewGroup,false); 128 | ViewHolder viewHolder = new ViewHolder(root); 129 | viewHolder.imageView = root.findViewById(R.id.grid_photo); 130 | // root.setTag(urls[i]); 131 | return viewHolder; 132 | } 133 | 134 | @Override 135 | public void onBindViewHolder(@NonNull ViewHolder viewHolder, int i) { 136 | ImageView imageView = viewHolder.imageView; 137 | String url = urls[i]; 138 | imageView.setTag(url); 139 | imageView.setImageResource(R.drawable.shape_item_empty); 140 | loadBitmaps(imageView, url); 141 | } 142 | 143 | /** 144 | * 加载Bitmap对象,如果Bitmap不在LruCache中,就开启线程去查询 145 | * 146 | * @param imageView 图片 147 | * @param url 地址 148 | */ 149 | private void loadBitmaps(ImageView imageView, String url) { 150 | Bitmap bitmap = getBitmapFromMemoryCache(url); 151 | if (bitmap != null) { 152 | if (imageView != null) { 153 | imageView.setImageBitmap(bitmap); 154 | } 155 | } else { 156 | service.execute(new ImageRunnable(url,imageView)); 157 | } 158 | } 159 | 160 | @Override 161 | public int getItemCount() { 162 | return urls.length; 163 | } 164 | 165 | /* 166 | 使用MD5算法进行加密 167 | */ 168 | public String hashKeyForDisk(String key) { 169 | String cacheKey; 170 | try { 171 | final MessageDigest mDigest = MessageDigest.getInstance("MD5"); 172 | mDigest.update(key.getBytes()); 173 | cacheKey = bytesToHexString(mDigest.digest()); 174 | } catch (NoSuchAlgorithmException e) { 175 | cacheKey = String.valueOf(key.hashCode()); 176 | } 177 | return cacheKey; 178 | } 179 | 180 | private String bytesToHexString(byte[] bytes) { 181 | StringBuilder sb = new StringBuilder(); 182 | for (int i = 0; i < bytes.length; i++) { 183 | String hex = Integer.toHexString(0xFF & bytes[i]); 184 | if (hex.length() == 1) { 185 | sb.append('0'); 186 | } 187 | sb.append(hex); 188 | } 189 | return sb.toString(); 190 | } 191 | 192 | /* 193 | 下载图片 194 | */ 195 | private boolean downloadImage(final String url, OutputStream outputStream) { 196 | Request request = new Request.Builder() 197 | .url(url) 198 | .build(); 199 | 200 | // 执行操作 201 | Call call = okHttpClient.newCall(request); 202 | Response response = null; 203 | BufferedInputStream in = null; 204 | BufferedOutputStream out = null; 205 | try { 206 | response = call.execute(); 207 | in = new BufferedInputStream(response.body().byteStream(), 8 * 1024); 208 | out = new BufferedOutputStream(outputStream, 8 * 1024); 209 | int b; 210 | while ((b = in.read()) != -1) { 211 | out.write(b); 212 | } 213 | return true; 214 | } catch (IOException e) { 215 | e.printStackTrace(); 216 | } finally { 217 | try { 218 | in.close(); 219 | out.close(); 220 | } catch (IOException e) { 221 | e.printStackTrace(); 222 | } 223 | } 224 | return false; 225 | } 226 | 227 | // 添加Bitmap到内存缓存中 228 | private void addBitmapToMemoryCache(Bitmap bitmap, String url) { 229 | if (getBitmapFromMemoryCache(url) == null) 230 | mMemoryCache.put(url, bitmap); 231 | } 232 | 233 | // 从内存缓存中获取Bitmap 234 | private Bitmap getBitmapFromMemoryCache(String url) { 235 | return mMemoryCache.get(url); 236 | } 237 | 238 | // 取消所有的下载程序 239 | public void cancelDownloadImage() { 240 | service.shutdown(); 241 | } 242 | 243 | // 将硬盘缓存同步到Journal文件中 244 | public void flushCache() { 245 | if (mDisLruCache != null) { 246 | try { 247 | mDisLruCache.flush(); 248 | } catch (IOException e) { 249 | e.printStackTrace(); 250 | } 251 | } 252 | } 253 | 254 | public class ViewHolder extends RecyclerView.ViewHolder{ 255 | 256 | public ImageView imageView; 257 | 258 | public ViewHolder(@NonNull View itemView) { 259 | super(itemView); 260 | } 261 | } 262 | 263 | public class ImageRunnable implements Runnable { 264 | private String url; 265 | private Bitmap bitmap; 266 | private ImageView mView; 267 | 268 | public ImageRunnable(String url,ImageView imageView) { 269 | this.url = url; 270 | this.mView = imageView; 271 | } 272 | 273 | @Override 274 | public void run() { 275 | FileDescriptor fileDescriptor = null; 276 | FileInputStream fileInputStream = null; 277 | DiskLruCache.Snapshot snapshot = null; 278 | final String key = hashKeyForDisk(url); 279 | // 查找key对应的硬盘缓存 280 | try { 281 | snapshot = mDisLruCache.get(key); 282 | if (snapshot == null) { 283 | // 如果对应的硬盘缓存没找到,就开始网络请求,并且写入缓存 284 | DiskLruCache.Editor editor = mDisLruCache.edit(key); 285 | if (editor != null) { 286 | OutputStream outputStream = editor.newOutputStream(0); 287 | if (downloadImage(url, outputStream)) { 288 | editor.commit(); 289 | } else { 290 | editor.abort(); 291 | } 292 | } 293 | snapshot = mDisLruCache.get(key); 294 | } 295 | 296 | if (snapshot != null) { 297 | fileInputStream = (FileInputStream) snapshot.getInputStream(0); 298 | fileDescriptor = fileInputStream.getFD(); 299 | } 300 | // 将缓存数据解析成Bitmap对象 301 | if (fileDescriptor != null) 302 | bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor); 303 | if (bitmap != null) { 304 | // 将图片添加到内存缓存中 305 | addBitmapToMemoryCache(bitmap, url); 306 | } 307 | if (bitmap != null) 308 | // 在主线程中更新 309 | mHandler.post(new Runnable() { 310 | @Override 311 | public void run() { 312 | /* ImageView image = mRecyclerView.findViewWithTag(url); 313 | if (image != null) 314 | image.setImageBitmap(bitmap);*/ 315 | mView.setImageBitmap(bitmap); 316 | } 317 | }); 318 | } catch (IOException e) { 319 | e.printStackTrace(); 320 | } 321 | } 322 | } 323 | } 324 | -------------------------------------------------------------------------------- /app/src/main/java/com/orient/test/adapter/MainAdapter.java: -------------------------------------------------------------------------------- 1 | package com.orient.test.adapter; 2 | 3 | import android.support.annotation.NonNull; 4 | import android.support.v7.widget.RecyclerView; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.TextView; 9 | 10 | import com.orient.test.R; 11 | 12 | /** 13 | * Author WangJie 14 | * Created on 2019/1/14. 15 | */ 16 | public class MainAdapter extends RecyclerView.Adapter { 17 | 18 | public static final String[] values = new String[]{ 19 | "书籍打开动画", 20 | "LruCache和DiskLruCache", 21 | "ConstraintLayout中Circular positioning" 22 | }; 23 | 24 | private OnSelectListener mListener; 25 | 26 | public MainAdapter(OnSelectListener mListener) { 27 | this.mListener = mListener; 28 | } 29 | 30 | @NonNull 31 | @Override 32 | public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) { 33 | View root = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.recycle_item_main, viewGroup, false); 34 | ViewHolder holder = new ViewHolder(root); 35 | return holder; 36 | } 37 | 38 | @Override 39 | public void onBindViewHolder(@NonNull ViewHolder viewHolder, int i) { 40 | final String s = values[i]; 41 | viewHolder.content = viewHolder.itemView.findViewById(R.id.txt_content); 42 | viewHolder.content.setText(s); 43 | viewHolder.content.setOnClickListener(new View.OnClickListener() { 44 | @Override 45 | public void onClick(View view) { 46 | mListener.onSelectStr(s); 47 | } 48 | }); 49 | } 50 | 51 | @Override 52 | public int getItemCount() { 53 | return values.length; 54 | } 55 | 56 | public class ViewHolder extends RecyclerView.ViewHolder { 57 | 58 | public TextView content; 59 | 60 | public ViewHolder(@NonNull View itemView) { 61 | super(itemView); 62 | } 63 | } 64 | 65 | public interface OnSelectListener{ 66 | void onSelectStr(String str); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /app/src/main/java/com/orient/test/adapter/PhotoAdapter.java: -------------------------------------------------------------------------------- 1 | package com.orient.test.adapter; 2 | 3 | import android.content.Context; 4 | import android.content.pm.PackageInfo; 5 | import android.content.pm.PackageManager; 6 | import android.graphics.Bitmap; 7 | import android.graphics.BitmapFactory; 8 | import android.os.Environment; 9 | import android.os.Handler; 10 | import android.support.annotation.NonNull; 11 | import android.support.annotation.Nullable; 12 | import android.support.v4.util.LruCache; 13 | import android.util.Log; 14 | import android.view.LayoutInflater; 15 | import android.view.View; 16 | import android.view.ViewGroup; 17 | import android.widget.ArrayAdapter; 18 | import android.widget.GridView; 19 | import android.widget.ImageView; 20 | 21 | import com.jakewharton.disklrucache.DiskLruCache; 22 | import com.orient.test.R; 23 | 24 | import java.io.BufferedInputStream; 25 | import java.io.BufferedOutputStream; 26 | import java.io.File; 27 | import java.io.FileDescriptor; 28 | import java.io.FileInputStream; 29 | import java.io.IOException; 30 | import java.io.OutputStream; 31 | import java.security.MessageDigest; 32 | import java.security.NoSuchAlgorithmException; 33 | import java.util.HashSet; 34 | import java.util.Set; 35 | import java.util.concurrent.ExecutorService; 36 | import java.util.concurrent.Executors; 37 | import java.util.concurrent.TimeUnit; 38 | 39 | import okhttp3.Call; 40 | import okhttp3.Callback; 41 | import okhttp3.ConnectionPool; 42 | import okhttp3.OkHttpClient; 43 | import okhttp3.Request; 44 | import okhttp3.Response; 45 | 46 | /** 47 | * Author WangJie 48 | * Created on 2019/1/18. 49 | */ 50 | public class PhotoAdapter extends ArrayAdapter { 51 | public static final String TAG = "PhotoAdapter"; 52 | 53 | private GridView mGridView; 54 | private Handler mHandler; 55 | private Context mContext; 56 | // 内存缓存 57 | private LruCache mMemoryCache; 58 | // 硬盘缓存 59 | private DiskLruCache mDiskLruCache; 60 | private OkHttpClient mOkHttpClient; 61 | // 线程池 62 | private ExecutorService service; 63 | // 记录每一个子项的高度 64 | private int mItemHeight = 0; 65 | 66 | public PhotoAdapter(@NonNull Context context, int resource, @NonNull String[] objects, GridView gridView, Handler handler) { 67 | super(context, resource, objects); 68 | mContext = context; 69 | mGridView = gridView; 70 | mHandler = handler; 71 | init(); 72 | } 73 | 74 | /* 75 | 初始化一些基本使用的配置 76 | */ 77 | private void init() { 78 | // 构造OkHttpClient并且设置线程池的数量 79 | mOkHttpClient = new OkHttpClient.Builder() 80 | .build(); 81 | 82 | // 构建一定线程数量的线程池 83 | service = Executors.newFixedThreadPool(6); 84 | 85 | // 获取应用可用的最大内存 86 | int maxMemory = (int) Runtime.getRuntime().maxMemory(); 87 | int cacheSize = maxMemory / 8; 88 | // 设置图片缓存的大小最大为缓存的1/8 89 | mMemoryCache = new LruCache(cacheSize) { 90 | @Override 91 | protected int sizeOf(@NonNull String key, @NonNull Bitmap value) { 92 | return value.getByteCount(); 93 | } 94 | }; 95 | // 设置硬盘缓存实例 96 | try { 97 | File file = getDiskCacheDir(mContext, "photo"); 98 | if (!file.exists()) 99 | file.mkdirs(); 100 | mDiskLruCache = DiskLruCache.open(file, getAppInfoVersion(), 1, 10 * 1024 * 1024); 101 | } catch (IOException e) { 102 | e.printStackTrace(); 103 | } 104 | } 105 | 106 | @NonNull 107 | @Override 108 | public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { 109 | final String url = getItem(position); 110 | View view; 111 | if (convertView != null) { 112 | view = convertView; 113 | } else { 114 | view = LayoutInflater.from(mContext).inflate(R.layout.recycle_item_net_work, parent, false); 115 | } 116 | final ImageView imageView = view.findViewById(R.id.grid_photo); 117 | if (imageView.getLayoutParams().height != mItemHeight) 118 | imageView.getLayoutParams().height = mItemHeight; 119 | imageView.setTag(url); 120 | imageView.setImageResource(R.drawable.shape_item_empty); 121 | loadBitmaps(imageView, url); 122 | return view; 123 | } 124 | 125 | /** 126 | * 加载Bitmap对象,如果Bitmap不在LruCache中,就开启线程去查询 127 | * 128 | * @param imageView 图片 129 | * @param url 地址 130 | */ 131 | private void loadBitmaps(ImageView imageView, String url) { 132 | //Bitmap bitmap = getBitmapFromMemoryCache(url); 133 | Bitmap bitmap = null; 134 | if (bitmap != null) { 135 | if (imageView != null) { 136 | imageView.setImageBitmap(bitmap); 137 | } 138 | } else { 139 | service.execute(new ImageRunnable(url)); 140 | } 141 | } 142 | 143 | 144 | /* 145 | 使用MD5算法进行加密 146 | */ 147 | public String hashKeyForDisk(String key) { 148 | String cacheKey; 149 | try { 150 | final MessageDigest mDigest = MessageDigest.getInstance("MD5"); 151 | mDigest.update(key.getBytes()); 152 | cacheKey = bytesToHexString(mDigest.digest()); 153 | } catch (NoSuchAlgorithmException e) { 154 | cacheKey = String.valueOf(key.hashCode()); 155 | } 156 | return cacheKey; 157 | } 158 | 159 | private String bytesToHexString(byte[] bytes) { 160 | StringBuilder sb = new StringBuilder(); 161 | for (int i = 0; i < bytes.length; i++) { 162 | String hex = Integer.toHexString(0xFF & bytes[i]); 163 | if (hex.length() == 1) { 164 | sb.append('0'); 165 | } 166 | sb.append(hex); 167 | } 168 | return sb.toString(); 169 | } 170 | 171 | private boolean downloadImage(final String url, OutputStream outputStream) { 172 | Request request = new Request.Builder() 173 | .url(url) 174 | .build(); 175 | 176 | // 执行操作 177 | Call call = mOkHttpClient.newCall(request); 178 | Response response = null; 179 | BufferedInputStream in = null; 180 | BufferedOutputStream out = null; 181 | try { 182 | response = call.execute(); 183 | in = new BufferedInputStream(response.body().byteStream(), 8 * 1024); 184 | out = new BufferedOutputStream(outputStream, 8 * 1024); 185 | int b; 186 | while ((b = in.read()) != -1) { 187 | out.write(b); 188 | } 189 | return true; 190 | } catch (IOException e) { 191 | e.printStackTrace(); 192 | } finally { 193 | try { 194 | in.close(); 195 | out.close(); 196 | } catch (IOException e) { 197 | e.printStackTrace(); 198 | } 199 | } 200 | return false; 201 | } 202 | 203 | // 取消所有的下载程序 204 | public void cancelDownloadImage() { 205 | service.shutdown(); 206 | } 207 | 208 | /* 209 | 根据传入的uniqueName获取唯一的硬盘的缓存路径 210 | */ 211 | public File getDiskCacheDir(Context context, String uniqueName) { 212 | String cachePath; 213 | if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) 214 | || !Environment.isExternalStorageRemovable()) { 215 | cachePath = context.getExternalCacheDir().getPath(); 216 | } else { 217 | cachePath = context.getCacheDir().getPath(); 218 | } 219 | return new File(cachePath + File.separator + uniqueName); 220 | } 221 | 222 | public int getAppInfoVersion() { 223 | try { 224 | PackageInfo info = mContext.getPackageManager().getPackageInfo(mContext.getPackageName(), 0); 225 | return info.versionCode; 226 | } catch (PackageManager.NameNotFoundException e) { 227 | e.printStackTrace(); 228 | } 229 | return 1; 230 | } 231 | 232 | // 添加Bitmap到内存缓存中 233 | private void addBitmapToMemoryCache(Bitmap bitmap, String url) { 234 | if (getBitmapFromMemoryCache(url) == null) 235 | mMemoryCache.put(url, bitmap); 236 | } 237 | 238 | // 从内存缓存中获取Bitmap 239 | private Bitmap getBitmapFromMemoryCache(String url) { 240 | return mMemoryCache.get(url); 241 | } 242 | 243 | // 设置高度 244 | public void setItemHeight(int height) { 245 | if (height == mItemHeight) { 246 | return; 247 | } 248 | mItemHeight = height; 249 | notifyDataSetChanged(); 250 | } 251 | 252 | // 将硬盘缓存同步到Journal文件中 253 | public void flushCache() { 254 | if (mDiskLruCache != null) { 255 | try { 256 | mDiskLruCache.flush(); 257 | } catch (IOException e) { 258 | e.printStackTrace(); 259 | } 260 | } 261 | } 262 | 263 | 264 | public class ImageRunnable implements Runnable { 265 | private String url; 266 | private Bitmap bitmap; 267 | 268 | public ImageRunnable(String url) { 269 | this.url = url; 270 | } 271 | 272 | @Override 273 | public void run() { 274 | FileDescriptor fileDescriptor = null; 275 | FileInputStream fileInputStream = null; 276 | DiskLruCache.Snapshot snapshot = null; 277 | final String key = hashKeyForDisk(url); 278 | // 查找key对应的硬盘缓存 279 | try { 280 | snapshot = mDiskLruCache.get(key); 281 | if (snapshot == null) { 282 | // 如果对应的硬盘缓存没找到,就开始网络请求,并且写入缓存 283 | DiskLruCache.Editor editor = mDiskLruCache.edit(key); 284 | if (editor != null) { 285 | OutputStream outputStream = editor.newOutputStream(0); 286 | if (downloadImage(url, outputStream)) { 287 | editor.commit(); 288 | } else { 289 | editor.abort(); 290 | } 291 | } 292 | snapshot = mDiskLruCache.get(key); 293 | } 294 | 295 | if (snapshot != null) { 296 | fileInputStream = (FileInputStream) snapshot.getInputStream(0); 297 | fileDescriptor = fileInputStream.getFD(); 298 | } 299 | // 将缓存数据解析成Bitmap对象 300 | if (fileDescriptor != null) 301 | bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor); 302 | if (bitmap != null) { 303 | // 将图片添加到内存缓存中 304 | addBitmapToMemoryCache(bitmap, url); 305 | } 306 | if (bitmap != null) 307 | // 在主线程中更新 308 | mHandler.post(new Runnable() { 309 | @Override 310 | public void run() { 311 | ImageView image = mGridView.findViewWithTag(url); 312 | if (image != null) 313 | image.setImageBitmap(bitmap); 314 | } 315 | }); 316 | } catch (IOException e) { 317 | e.printStackTrace(); 318 | } 319 | } 320 | } 321 | 322 | 323 | } 324 | -------------------------------------------------------------------------------- /app/src/main/java/com/orient/test/animation/ContentScaleAnimation.java: -------------------------------------------------------------------------------- 1 | package com.orient.test.animation; 2 | 3 | import android.graphics.Matrix; 4 | import android.view.animation.Animation; 5 | import android.view.animation.Transformation; 6 | 7 | public class ContentScaleAnimation extends Animation { 8 | private float mPivotX; 9 | private float mPivotY; 10 | private float mPivotXValue; // 控件左上角X 11 | private float mPivotYValue; 12 | private final float scaleTimes; 13 | private boolean mReverse; 14 | 15 | public ContentScaleAnimation(float mPivotXValue, float mPivotYValue, float scaleTimes, boolean mReverse) { 16 | 17 | this.mPivotXValue = mPivotXValue; 18 | this.mPivotYValue = mPivotYValue; 19 | this.scaleTimes = scaleTimes; 20 | this.mReverse = mReverse; 21 | } 22 | 23 | @Override 24 | protected void applyTransformation(float interpolatedTime, Transformation t) { 25 | Matrix matrix=t.getMatrix();//缩放方法 26 | if (mReverse) { 27 | matrix.postScale(1 + (scaleTimes - 1) * (1.0f - interpolatedTime), 1 + (scaleTimes - 1) * (1.0f - interpolatedTime), mPivotX - mPivotXValue, mPivotY - mPivotYValue); 28 | } else { 29 | matrix.postScale(1 + (scaleTimes - 1) * interpolatedTime, 1 + (scaleTimes - 1) * interpolatedTime, mPivotX - mPivotXValue , mPivotY - mPivotYValue ); 30 | } 31 | } 32 | //缩放点坐标值 33 | @Override 34 | public void initialize(int width, int height, int parentWidth, int parentHeight) { 35 | super.initialize(width, height, parentWidth, parentHeight); 36 | mPivotX = resolvePivotX(mPivotXValue, parentWidth, width); 37 | mPivotY = resolvePivoY(mPivotYValue, parentHeight, height); 38 | } 39 | //缩放点坐标值 缩放点到自身左边距离/缩放点到父控件左边的距离=缩放点自身右侧距离/缩放点到父控件右边的距离 40 | private float resolvePivotX(float margingLeft, int parentWidth, int width) { 41 | return (margingLeft * parentWidth) / (parentWidth - width); 42 | } 43 | 44 | private float resolvePivoY(float marginTop, int parentHeight, int height) { 45 | return (marginTop * parentHeight) / (parentHeight - height); 46 | } 47 | 48 | public void reverse() { 49 | mReverse = !mReverse; 50 | } 51 | 52 | public boolean getMReverse() { 53 | return mReverse; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /app/src/main/java/com/orient/test/animation/Rotate3DAnimation.java: -------------------------------------------------------------------------------- 1 | package com.orient.test.animation; 2 | 3 | import android.content.Context; 4 | import android.graphics.Camera; 5 | import android.graphics.Matrix; 6 | import android.util.Log; 7 | import android.view.animation.Animation; 8 | import android.view.animation.Transformation; 9 | 10 | /** 11 | * Author WangJie 12 | * Created on 2019/1/15. 13 | */ 14 | public class Rotate3DAnimation extends Animation { 15 | private static final String TAG = "Rotate3DAnimation"; 16 | 17 | private final float mFromDegrees; 18 | private final float mToDegrees; 19 | private final float mMarginLeft; 20 | private final float mMarginTop; 21 | // private final float mDepthZ; 22 | private final float mAnimationScale; 23 | private boolean reverse; 24 | private Camera mCamera; 25 | 26 | // 旋转中心 27 | private float mPivotX; 28 | private float mPivotY; 29 | 30 | private float scale = 1; // <------- 像素密度 31 | 32 | public Rotate3DAnimation(Context context, float mFromDegrees, float mToDegrees, float mMarginLeft, float mMarginTop, 33 | float animationScale, boolean reverse) { 34 | this.mFromDegrees = mFromDegrees; 35 | this.mToDegrees = mToDegrees; 36 | this.mMarginLeft = mMarginLeft; 37 | this.mMarginTop = mMarginTop; 38 | this.mAnimationScale = animationScale; 39 | this.reverse = reverse; 40 | 41 | // 获取手机像素密度 (即dp与px的比例) 42 | scale = context.getResources().getDisplayMetrics().density; 43 | } 44 | 45 | @Override 46 | public void initialize(int width, int height, int parentWidth, int parentHeight) { 47 | super.initialize(width, height, parentWidth, parentHeight); 48 | 49 | mCamera = new Camera(); 50 | mPivotX = calculatePivotX(mMarginLeft, parentWidth, width); 51 | mPivotY = calculatePivotY(mMarginTop, parentHeight, height); 52 | Log.i(TAG,"width:"+width+",height:"+height+",pw:"+parentWidth+",ph:"+parentHeight); 53 | Log.i(TAG,"中心点x:"+mPivotX+",中心点y:"+mPivotY); 54 | } 55 | 56 | @Override 57 | protected void applyTransformation(float interpolatedTime, Transformation t) { 58 | super.applyTransformation(interpolatedTime, t); 59 | 60 | float degrees = reverse ? mToDegrees + (mFromDegrees - mToDegrees) * interpolatedTime : mFromDegrees + (mToDegrees - mFromDegrees) * interpolatedTime; 61 | Matrix matrix = t.getMatrix(); 62 | 63 | Camera camera = mCamera; 64 | camera.save(); 65 | camera.rotateY(degrees); 66 | camera.getMatrix(matrix); 67 | camera.restore(); 68 | 69 | 70 | // 修正失真,主要修改 MPERSP_0 和 MPERSP_1 71 | float[] mValues = new float[9]; 72 | matrix.getValues(mValues); //获取数值 73 | mValues[6] = mValues[6] / scale; //数值修正 74 | mValues[7] = mValues[7] / scale; //数值修正 75 | matrix.setValues(mValues); //重新赋值 76 | 77 | if (reverse) { 78 | matrix.postScale(1 + (mAnimationScale - 1) * interpolatedTime, 1 + (mAnimationScale - 1) * interpolatedTime, 79 | mPivotX - mMarginLeft, mPivotY - mMarginTop); 80 | } else { 81 | matrix.postScale(1 + (mAnimationScale - 1) * (1 - interpolatedTime), 1 + (mAnimationScale - 1) * (1 - interpolatedTime), 82 | mPivotX - mMarginLeft, mPivotY - mMarginTop); 83 | } 84 | } 85 | 86 | /** 87 | * 计算缩放的中心点的横坐标 88 | * 89 | * @param marginLeft 该View距离父布局左边的距离 90 | * @param parentWidth 父布局的宽度 91 | * @param width View的宽度 92 | * @return 缩放中心点的横坐标 93 | */ 94 | public float calculatePivotX(float marginLeft, float parentWidth, float width) { 95 | return parentWidth * marginLeft / (parentWidth - width); 96 | } 97 | 98 | 99 | /** 100 | * 计算缩放的中心点的纵坐标 101 | * 102 | * @param marginTop 该View顶部距离父布局顶部的距离 103 | * @param parentHeight 父布局的高度 104 | * @param height 子布局的高度 105 | * @return 缩放的中心点的纵坐标 106 | */ 107 | public float calculatePivotY(float marginTop, float parentHeight, float height) { 108 | return parentHeight * marginTop / (parentHeight - height); 109 | } 110 | 111 | public void reverse() { 112 | reverse = !reverse; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /app/src/main/java/com/orient/test/common/Image.java: -------------------------------------------------------------------------------- 1 | package com.orient.test.common; 2 | 3 | /** 4 | * Author WangJie 5 | * Created on 2019/1/18. 6 | */ 7 | public class Image { 8 | public final static String[] imageThumbUrls = new String[] { 9 | "https://img-my.csdn.net/uploads/201407/26/1406383299_1976.jpg", 10 | "https://img-my.csdn.net/uploads/201407/26/1406383291_6518.jpg", 11 | "https://img-my.csdn.net/uploads/201407/26/1406383291_8239.jpg", 12 | "https://img-my.csdn.net/uploads/201407/26/1406383290_9329.jpg", 13 | "https://img-my.csdn.net/uploads/201407/26/1406383290_1042.jpg", 14 | "https://img-my.csdn.net/uploads/201407/26/1406383275_3977.jpg", 15 | "https://img-my.csdn.net/uploads/201407/26/1406383265_8550.jpg", 16 | "https://img-my.csdn.net/uploads/201407/26/1406383264_3954.jpg", 17 | "https://img-my.csdn.net/uploads/201407/26/1406383264_4787.jpg", 18 | "https://img-my.csdn.net/uploads/201407/26/1406383264_8243.jpg", 19 | "https://img-my.csdn.net/uploads/201407/26/1406383248_3693.jpg", 20 | "https://img-my.csdn.net/uploads/201407/26/1406383243_5120.jpg", 21 | "https://img-my.csdn.net/uploads/201407/26/1406383242_3127.jpg", 22 | "https://img-my.csdn.net/uploads/201407/26/1406383242_9576.jpg", 23 | "https://img-my.csdn.net/uploads/201407/26/1406383242_1721.jpg", 24 | "https://img-my.csdn.net/uploads/201407/26/1406383219_5806.jpg", 25 | "https://img-my.csdn.net/uploads/201407/26/1406383214_7794.jpg", 26 | "https://img-my.csdn.net/uploads/201407/26/1406383213_4418.jpg", 27 | "https://img-my.csdn.net/uploads/201407/26/1406383213_3557.jpg", 28 | "https://img-my.csdn.net/uploads/201407/26/1406383210_8779.jpg", 29 | "https://img-my.csdn.net/uploads/201407/26/1406383172_4577.jpg", 30 | "https://img-my.csdn.net/uploads/201407/26/1406383166_3407.jpg", 31 | "https://img-my.csdn.net/uploads/201407/26/1406383166_2224.jpg", 32 | "https://img-my.csdn.net/uploads/201407/26/1406383166_7301.jpg", 33 | "https://img-my.csdn.net/uploads/201407/26/1406383165_7197.jpg", 34 | "https://img-my.csdn.net/uploads/201407/26/1406383150_8410.jpg", 35 | "https://img-my.csdn.net/uploads/201407/26/1406383131_3736.jpg", 36 | "https://img-my.csdn.net/uploads/201407/26/1406383130_5094.jpg", 37 | "https://img-my.csdn.net/uploads/201407/26/1406383130_7393.jpg", 38 | "https://img-my.csdn.net/uploads/201407/26/1406383129_8813.jpg", 39 | "https://img-my.csdn.net/uploads/201407/26/1406383100_3554.jpg", 40 | "https://img-my.csdn.net/uploads/201407/26/1406383093_7894.jpg", 41 | "https://img-my.csdn.net/uploads/201407/26/1406383092_2432.jpg", 42 | "https://img-my.csdn.net/uploads/201407/26/1406383092_3071.jpg", 43 | "https://img-my.csdn.net/uploads/201407/26/1406383091_3119.jpg", 44 | "https://img-my.csdn.net/uploads/201407/26/1406383059_6589.jpg", 45 | "https://img-my.csdn.net/uploads/201407/26/1406383059_8814.jpg", 46 | "https://img-my.csdn.net/uploads/201407/26/1406383059_2237.jpg", 47 | "https://img-my.csdn.net/uploads/201407/26/1406383058_4330.jpg", 48 | "https://img-my.csdn.net/uploads/201407/26/1406383038_3602.jpg", 49 | "https://img-my.csdn.net/uploads/201407/26/1406382942_3079.jpg", 50 | "https://img-my.csdn.net/uploads/201407/26/1406382942_8125.jpg", 51 | "https://img-my.csdn.net/uploads/201407/26/1406382942_4881.jpg", 52 | "https://img-my.csdn.net/uploads/201407/26/1406382941_4559.jpg", 53 | "https://img-my.csdn.net/uploads/201407/26/1406382941_3845.jpg", 54 | "https://img-my.csdn.net/uploads/201407/26/1406382924_8955.jpg", 55 | "https://img-my.csdn.net/uploads/201407/26/1406382923_2141.jpg", 56 | "https://img-my.csdn.net/uploads/201407/26/1406382923_8437.jpg", 57 | "https://img-my.csdn.net/uploads/201407/26/1406382922_6166.jpg", 58 | "https://img-my.csdn.net/uploads/201407/26/1406382922_4843.jpg", 59 | "https://img-my.csdn.net/uploads/201407/26/1406382905_5804.jpg", 60 | "https://img-my.csdn.net/uploads/201407/26/1406382904_3362.jpg", 61 | "https://img-my.csdn.net/uploads/201407/26/1406382904_2312.jpg", 62 | "https://img-my.csdn.net/uploads/201407/26/1406382904_4960.jpg", 63 | "https://img-my.csdn.net/uploads/201407/26/1406382900_2418.jpg", 64 | "https://img-my.csdn.net/uploads/201407/26/1406382881_4490.jpg", 65 | "https://img-my.csdn.net/uploads/201407/26/1406382881_5935.jpg", 66 | "https://img-my.csdn.net/uploads/201407/26/1406382880_3865.jpg", 67 | "https://img-my.csdn.net/uploads/201407/26/1406382880_4662.jpg", 68 | "https://img-my.csdn.net/uploads/201407/26/1406382879_2553.jpg", 69 | "https://img-my.csdn.net/uploads/201407/26/1406382862_5375.jpg", 70 | "https://img-my.csdn.net/uploads/201407/26/1406382862_1748.jpg", 71 | "https://img-my.csdn.net/uploads/201407/26/1406382861_7618.jpg", 72 | "https://img-my.csdn.net/uploads/201407/26/1406382861_8606.jpg", 73 | "https://img-my.csdn.net/uploads/201407/26/1406382861_8949.jpg", 74 | "https://img-my.csdn.net/uploads/201407/26/1406382841_9821.jpg", 75 | "https://img-my.csdn.net/uploads/201407/26/1406382840_6603.jpg", 76 | "https://img-my.csdn.net/uploads/201407/26/1406382840_2405.jpg", 77 | "https://img-my.csdn.net/uploads/201407/26/1406382840_6354.jpg", 78 | "https://img-my.csdn.net/uploads/201407/26/1406382839_5779.jpg", 79 | "https://img-my.csdn.net/uploads/201407/26/1406382810_7578.jpg", 80 | "https://img-my.csdn.net/uploads/201407/26/1406382810_2436.jpg", 81 | "https://img-my.csdn.net/uploads/201407/26/1406382809_3883.jpg", 82 | "https://img-my.csdn.net/uploads/201407/26/1406382809_6269.jpg", 83 | "https://img-my.csdn.net/uploads/201407/26/1406382808_4179.jpg", 84 | "https://img-my.csdn.net/uploads/201407/26/1406382790_8326.jpg", 85 | "https://img-my.csdn.net/uploads/201407/26/1406382789_7174.jpg", 86 | "https://img-my.csdn.net/uploads/201407/26/1406382789_5170.jpg", 87 | "https://img-my.csdn.net/uploads/201407/26/1406382789_4118.jpg", 88 | "https://img-my.csdn.net/uploads/201407/26/1406382788_9532.jpg", 89 | "https://img-my.csdn.net/uploads/201407/26/1406382767_3184.jpg", 90 | "https://img-my.csdn.net/uploads/201407/26/1406382767_4772.jpg", 91 | "https://img-my.csdn.net/uploads/201407/26/1406382766_4924.jpg", 92 | "https://img-my.csdn.net/uploads/201407/26/1406382766_5762.jpg", 93 | "https://img-my.csdn.net/uploads/201407/26/1406382765_7341.jpg" 94 | }; 95 | } 96 | -------------------------------------------------------------------------------- /app/src/main/java/com/orient/test/ui/activity/BookSampleActivity.java: -------------------------------------------------------------------------------- 1 | package com.orient.test.ui.activity; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.os.Bundle; 7 | import android.view.View; 8 | import android.widget.Button; 9 | 10 | import com.orient.test.R; 11 | 12 | public class BookSampleActivity extends AppCompatActivity { 13 | 14 | // 结束当前界面 15 | private Button mBtnFinish; 16 | 17 | public static void show(Context context){ 18 | Intent intent = new Intent(context,BookSampleActivity.class); 19 | context.startActivity(intent); 20 | } 21 | 22 | @Override 23 | protected void onCreate(Bundle savedInstanceState) { 24 | super.onCreate(savedInstanceState); 25 | setContentView(R.layout.activity_book_sample); 26 | 27 | initWidget(); 28 | } 29 | 30 | // 初始化布局 31 | private void initWidget() { 32 | mBtnFinish = findViewById(R.id.btn_finish); 33 | mBtnFinish.setOnClickListener(new View.OnClickListener() { 34 | @Override 35 | public void onClick(View v) { 36 | finish(); 37 | } 38 | }); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/orient/test/ui/activity/ConstraintActivity.java: -------------------------------------------------------------------------------- 1 | package com.orient.test.ui.activity; 2 | 3 | import android.animation.Animator; 4 | import android.animation.AnimatorSet; 5 | import android.animation.ValueAnimator; 6 | import android.annotation.SuppressLint; 7 | import android.content.Context; 8 | import android.content.Intent; 9 | import android.support.constraint.ConstraintLayout; 10 | import android.support.constraint.Constraints; 11 | import android.support.constraint.Group; 12 | import android.support.design.widget.FloatingActionButton; 13 | import android.support.v7.app.AppCompatActivity; 14 | import android.os.Bundle; 15 | import android.view.View; 16 | import android.view.animation.DecelerateInterpolator; 17 | 18 | import com.orient.test.R; 19 | import com.orient.test.utils.UiUtils; 20 | 21 | public class ConstraintActivity extends AppCompatActivity { 22 | 23 | private FloatingActionButton mAdd; 24 | private FloatingActionButton mLike; 25 | private FloatingActionButton mWrite; 26 | private FloatingActionButton mTop; 27 | private Group likeGroup; 28 | private Group writeGroup; 29 | private Group topGroup; 30 | // 动画集合,用来控制动画的有序播放 31 | private AnimatorSet animatorSet; 32 | // 圆的半径 33 | private int radius; 34 | // FloatingActionButton宽度和高度,宽高一样 35 | private int width; 36 | 37 | public static void show(Context context) { 38 | Intent intent = new Intent(context, ConstraintActivity.class); 39 | context.startActivity(intent); 40 | } 41 | 42 | @Override 43 | protected void onCreate(Bundle savedInstanceState) { 44 | super.onCreate(savedInstanceState); 45 | setContentView(R.layout.activity_constraint); 46 | 47 | initWidget(); 48 | initListener(); 49 | } 50 | 51 | @Override 52 | protected void onResume() { 53 | super.onResume(); 54 | 55 | // 动态获取FloatingActionButton的宽 56 | mAdd.post(new Runnable() { 57 | @Override 58 | public void run() { 59 | width = mAdd.getMeasuredWidth(); 60 | } 61 | }); 62 | // 在xml文件里设置的半径 63 | radius = UiUtils.dp2px(this, 80); 64 | } 65 | 66 | private void initWidget() { 67 | mAdd = findViewById(R.id.fab_add); 68 | mLike = findViewById(R.id.fab_like); 69 | mTop = findViewById(R.id.fab_top); 70 | mWrite = findViewById(R.id.fab_write); 71 | likeGroup = findViewById(R.id.gp_like); 72 | writeGroup = findViewById(R.id.gp_write); 73 | topGroup = findViewById(R.id.gp_top); 74 | // 将三个弹出的FloatingActionButton隐藏 75 | setViewVisible(false); 76 | } 77 | 78 | private void setViewVisible(boolean isShow) { 79 | likeGroup.setVisibility(isShow?View.VISIBLE:View.GONE); 80 | writeGroup.setVisibility(isShow?View.VISIBLE:View.GONE); 81 | topGroup.setVisibility(isShow?View.VISIBLE:View.GONE); 82 | } 83 | 84 | private void initListener() { 85 | mAdd.setOnClickListener(new View.OnClickListener() { 86 | @Override 87 | public void onClick(View v) { 88 | // 播放动画的时候不可以点击 89 | if(animatorSet != null && animatorSet.isRunning()) 90 | return; 91 | 92 | // 判断播放显示还是隐藏动画 93 | if(likeGroup.getVisibility() != View.VISIBLE) { 94 | animatorSet = new AnimatorSet(); 95 | ValueAnimator likeAnimator = getValueAnimator(mLike, false, likeGroup,0); 96 | ValueAnimator writeAnimator = getValueAnimator(mWrite, false, writeGroup,45); 97 | ValueAnimator topAnimator = getValueAnimator(mTop, false, topGroup,90); 98 | animatorSet.playSequentially(likeAnimator, writeAnimator, topAnimator); 99 | animatorSet.start(); 100 | }else { 101 | animatorSet = new AnimatorSet(); 102 | ValueAnimator likeAnimator = getValueAnimator(mLike, true, likeGroup,0); 103 | ValueAnimator writeAnimator = getValueAnimator(mWrite, true, writeGroup,45); 104 | ValueAnimator topAnimator = getValueAnimator(mTop, true, topGroup,90); 105 | animatorSet.playSequentially(topAnimator, writeAnimator, likeAnimator); 106 | animatorSet.start(); 107 | } 108 | 109 | } 110 | }); 111 | } 112 | 113 | /** 114 | * 获取ValueAnimator 115 | * 116 | * @param button FloatingActionButton 117 | * @param reverse 开始还是隐藏 118 | * @param group Group 119 | * @param angle angle 转动的角度 120 | * @return ValueAnimator 121 | */ 122 | private ValueAnimator getValueAnimator(final FloatingActionButton button, final boolean reverse, final Group group, final int angle) { 123 | ValueAnimator animator; 124 | if (reverse) 125 | animator = ValueAnimator.ofFloat(1, 0); 126 | else 127 | animator = ValueAnimator.ofFloat(0, 1); 128 | animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 129 | @Override 130 | public void onAnimationUpdate(ValueAnimator animation) { 131 | float v = (float) animation.getAnimatedValue(); 132 | ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) button.getLayoutParams(); 133 | params.circleRadius = (int) (radius * v); 134 | params.circleAngle = 270f + angle * v; 135 | params.width = (int) (width * v); 136 | params.height = (int) (width * v); 137 | button.setLayoutParams(params); 138 | } 139 | }); 140 | animator.addListener(new SimpleAnimation() { 141 | @Override 142 | public void onAnimationStart(Animator animation) { 143 | group.setVisibility(View.VISIBLE); 144 | } 145 | 146 | @Override 147 | public void onAnimationEnd(Animator animation) { 148 | if(group == likeGroup && reverse){ 149 | setViewVisible(false); 150 | } 151 | } 152 | }); 153 | animator.setDuration(300); 154 | animator.setInterpolator(new DecelerateInterpolator()); 155 | return animator; 156 | } 157 | 158 | abstract class SimpleAnimation implements Animator.AnimatorListener{ 159 | @Override 160 | public void onAnimationStart(Animator animation) { 161 | 162 | } 163 | 164 | @Override 165 | public void onAnimationEnd(Animator animation) { 166 | 167 | } 168 | 169 | @Override 170 | public void onAnimationCancel(Animator animation) { 171 | 172 | } 173 | 174 | @Override 175 | public void onAnimationRepeat(Animator animation) { 176 | 177 | } 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /app/src/main/java/com/orient/test/ui/activity/NetWorkActivity.java: -------------------------------------------------------------------------------- 1 | package com.orient.test.ui.activity; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.os.Handler; 7 | import android.os.Looper; 8 | import android.support.v7.app.AppCompatActivity; 9 | import android.support.v7.widget.GridLayoutManager; 10 | import android.support.v7.widget.RecyclerView; 11 | 12 | import com.orient.test.R; 13 | import com.orient.test.adapter.GridPhotoAdapter; 14 | 15 | /* 16 | 照片墙的例子 结合LruCache和DisLruCache使用 17 | */ 18 | public class NetWorkActivity extends AppCompatActivity { 19 | 20 | public final static String[] imageThumbUrls = new String[]{ 21 | "https://img-my.csdn.net/uploads/201407/26/1406383299_1976.jpg", 22 | "https://img-my.csdn.net/uploads/201407/26/1406383291_6518.jpg", 23 | "https://img-my.csdn.net/uploads/201407/26/1406383291_8239.jpg", 24 | "https://img-my.csdn.net/uploads/201407/26/1406383290_9329.jpg", 25 | "https://img-my.csdn.net/uploads/201407/26/1406383290_1042.jpg", 26 | "https://img-my.csdn.net/uploads/201407/26/1406383275_3977.jpg", 27 | "https://img-my.csdn.net/uploads/201407/26/1406383265_8550.jpg", 28 | "https://img-my.csdn.net/uploads/201407/26/1406383264_3954.jpg", 29 | "https://img-my.csdn.net/uploads/201407/26/1406383264_4787.jpg", 30 | "https://img-my.csdn.net/uploads/201407/26/1406383264_8243.jpg", 31 | "https://img-my.csdn.net/uploads/201407/26/1406383248_3693.jpg", 32 | "https://img-my.csdn.net/uploads/201407/26/1406383243_5120.jpg", 33 | "https://img-my.csdn.net/uploads/201407/26/1406383242_3127.jpg", 34 | "https://img-my.csdn.net/uploads/201407/26/1406383242_9576.jpg", 35 | "https://img-my.csdn.net/uploads/201407/26/1406383242_1721.jpg", 36 | "https://img-my.csdn.net/uploads/201407/26/1406383219_5806.jpg", 37 | "https://img-my.csdn.net/uploads/201407/26/1406383214_7794.jpg", 38 | "https://img-my.csdn.net/uploads/201407/26/1406383213_4418.jpg", 39 | "https://img-my.csdn.net/uploads/201407/26/1406383213_3557.jpg", 40 | "https://img-my.csdn.net/uploads/201407/26/1406383210_8779.jpg", 41 | "https://img-my.csdn.net/uploads/201407/26/1406383172_4577.jpg", 42 | "https://img-my.csdn.net/uploads/201407/26/1406383166_3407.jpg", 43 | "https://img-my.csdn.net/uploads/201407/26/1406383166_2224.jpg", 44 | "https://img-my.csdn.net/uploads/201407/26/1406383166_7301.jpg", 45 | "https://img-my.csdn.net/uploads/201407/26/1406383165_7197.jpg", 46 | "https://img-my.csdn.net/uploads/201407/26/1406383150_8410.jpg", 47 | "https://img-my.csdn.net/uploads/201407/26/1406383131_3736.jpg", 48 | "https://img-my.csdn.net/uploads/201407/26/1406383130_5094.jpg", 49 | "https://img-my.csdn.net/uploads/201407/26/1406383130_7393.jpg", 50 | "https://img-my.csdn.net/uploads/201407/26/1406383129_8813.jpg", 51 | "https://img-my.csdn.net/uploads/201407/26/1406383100_3554.jpg", 52 | "https://img-my.csdn.net/uploads/201407/26/1406383093_7894.jpg", 53 | "https://img-my.csdn.net/uploads/201407/26/1406383092_2432.jpg", 54 | "https://img-my.csdn.net/uploads/201407/26/1406383092_3071.jpg", 55 | "https://img-my.csdn.net/uploads/201407/26/1406383091_3119.jpg", 56 | "https://img-my.csdn.net/uploads/201407/26/1406383059_6589.jpg", 57 | "https://img-my.csdn.net/uploads/201407/26/1406383059_8814.jpg", 58 | "https://img-my.csdn.net/uploads/201407/26/1406383059_2237.jpg", 59 | "https://img-my.csdn.net/uploads/201407/26/1406383058_4330.jpg", 60 | "https://img-my.csdn.net/uploads/201407/26/1406383038_3602.jpg", 61 | "https://img-my.csdn.net/uploads/201407/26/1406382942_3079.jpg", 62 | "https://img-my.csdn.net/uploads/201407/26/1406382942_8125.jpg", 63 | "https://img-my.csdn.net/uploads/201407/26/1406382942_4881.jpg", 64 | "https://img-my.csdn.net/uploads/201407/26/1406382941_4559.jpg", 65 | "https://img-my.csdn.net/uploads/201407/26/1406382941_3845.jpg", 66 | "https://img-my.csdn.net/uploads/201407/26/1406382924_8955.jpg", 67 | "https://img-my.csdn.net/uploads/201407/26/1406382923_2141.jpg", 68 | "https://img-my.csdn.net/uploads/201407/26/1406382923_8437.jpg", 69 | "https://img-my.csdn.net/uploads/201407/26/1406382922_6166.jpg", 70 | "https://img-my.csdn.net/uploads/201407/26/1406382922_4843.jpg", 71 | "https://img-my.csdn.net/uploads/201407/26/1406382905_5804.jpg", 72 | "https://img-my.csdn.net/uploads/201407/26/1406382904_3362.jpg", 73 | "https://img-my.csdn.net/uploads/201407/26/1406382904_2312.jpg", 74 | "https://img-my.csdn.net/uploads/201407/26/1406382904_4960.jpg", 75 | "https://img-my.csdn.net/uploads/201407/26/1406382900_2418.jpg", 76 | "https://img-my.csdn.net/uploads/201407/26/1406382881_4490.jpg", 77 | "https://img-my.csdn.net/uploads/201407/26/1406382881_5935.jpg", 78 | "https://img-my.csdn.net/uploads/201407/26/1406382880_3865.jpg", 79 | "https://img-my.csdn.net/uploads/201407/26/1406382880_4662.jpg", 80 | "https://img-my.csdn.net/uploads/201407/26/1406382879_2553.jpg", 81 | "https://img-my.csdn.net/uploads/201407/26/1406382862_5375.jpg", 82 | "https://img-my.csdn.net/uploads/201407/26/1406382862_1748.jpg", 83 | "https://img-my.csdn.net/uploads/201407/26/1406382861_7618.jpg", 84 | "https://img-my.csdn.net/uploads/201407/26/1406382861_8606.jpg", 85 | "https://img-my.csdn.net/uploads/201407/26/1406382861_8949.jpg", 86 | "https://img-my.csdn.net/uploads/201407/26/1406382841_9821.jpg", 87 | "https://img-my.csdn.net/uploads/201407/26/1406382840_6603.jpg", 88 | "https://img-my.csdn.net/uploads/201407/26/1406382840_2405.jpg", 89 | "https://img-my.csdn.net/uploads/201407/26/1406382840_6354.jpg", 90 | "https://img-my.csdn.net/uploads/201407/26/1406382839_5779.jpg", 91 | "https://img-my.csdn.net/uploads/201407/26/1406382810_7578.jpg", 92 | "https://img-my.csdn.net/uploads/201407/26/1406382810_2436.jpg", 93 | "https://img-my.csdn.net/uploads/201407/26/1406382809_3883.jpg", 94 | "https://img-my.csdn.net/uploads/201407/26/1406382809_6269.jpg", 95 | "https://img-my.csdn.net/uploads/201407/26/1406382808_4179.jpg", 96 | "https://img-my.csdn.net/uploads/201407/26/1406382790_8326.jpg", 97 | "https://img-my.csdn.net/uploads/201407/26/1406382789_7174.jpg", 98 | "https://img-my.csdn.net/uploads/201407/26/1406382789_5170.jpg", 99 | "https://img-my.csdn.net/uploads/201407/26/1406382789_4118.jpg", 100 | "https://img-my.csdn.net/uploads/201407/26/1406382788_9532.jpg", 101 | "https://img-my.csdn.net/uploads/201407/26/1406382767_3184.jpg", 102 | "https://img-my.csdn.net/uploads/201407/26/1406382767_4772.jpg", 103 | "https://img-my.csdn.net/uploads/201407/26/1406382766_4924.jpg", 104 | "https://img-my.csdn.net/uploads/201407/26/1406382766_5762.jpg", 105 | "https://img-my.csdn.net/uploads/201407/26/1406382765_7341.jpg" 106 | }; 107 | 108 | // 完全用Glide时候的适配器,实现的效果相同 109 | //private GlideAdapter mAdapter; 110 | private GridPhotoAdapter mAdapter; 111 | 112 | //private int mImageThumbSize; 113 | //private int mImageThumbSpacing; 114 | 115 | public static void show(Context context) { 116 | Intent intent = new Intent(context, NetWorkActivity.class); 117 | context.startActivity(intent); 118 | } 119 | 120 | @Override 121 | protected void onCreate(Bundle savedInstanceState) { 122 | super.onCreate(savedInstanceState); 123 | setContentView(R.layout.activity_net_work); 124 | 125 | initWidget(); 126 | 127 | } 128 | 129 | private void initWidget() { 130 | RecyclerView mRecyclerView = findViewById(R.id.recycle); 131 | Handler mHandler = new Handler(Looper.getMainLooper()); 132 | mRecyclerView.setLayoutManager(new GridLayoutManager(this, 3)); 133 | mRecyclerView.setAdapter(mAdapter = new GridPhotoAdapter(imageThumbUrls, mHandler,this)); 134 | //mRecyclerView.setAdapter(mAdapter = new GlideAdapter(imageThumbUrls, this)); 135 | //mGridView = findViewById(R.id.grid_photo); 136 | /*mImageThumbSize = getResources().getDimensionPixelSize( 137 | R.dimen.common_size); 138 | mImageThumbSpacing = getResources().getDimensionPixelSize( 139 | R.dimen.common_size); 140 | mHandler = new Handler(Looper.getMainLooper()); 141 | mAdapter = new PhotoAdapter(this, R.layout.recycle_item_net_work, Image.imageThumbUrls, mGridView, mHandler); 142 | mAdapter.addAll(); 143 | mGridView.setAdapter(mAdapter); 144 | mGridView.getViewTreeObserver().addOnGlobalLayoutListener( 145 | new ViewTreeObserver.OnGlobalLayoutListener() { 146 | 147 | @Override 148 | public void onGlobalLayout() { 149 | final int numColumns = (int) Math.floor(mGridView 150 | .getWidth() 151 | / (mImageThumbSize + mImageThumbSpacing)); 152 | if (numColumns > 0) { 153 | int columnWidth = (mGridView.getWidth() / numColumns) 154 | - mImageThumbSpacing; 155 | mAdapter.setItemHeight(columnWidth); 156 | mGridView.getViewTreeObserver() 157 | .removeGlobalOnLayoutListener(this); 158 | } 159 | } 160 | });*/ 161 | 162 | } 163 | 164 | @Override 165 | protected void onPause() { 166 | super.onPause(); 167 | // 将日志同步到journal文件中 168 | mAdapter.flushCache(); 169 | } 170 | 171 | @Override 172 | protected void onDestroy() { 173 | super.onDestroy(); 174 | // 退出程序时结束所有的下载任务 175 | mAdapter.cancelDownloadImage(); 176 | } 177 | 178 | 179 | } 180 | -------------------------------------------------------------------------------- /app/src/main/java/com/orient/test/ui/activity/OpenBookActivity.java: -------------------------------------------------------------------------------- 1 | package com.orient.test.ui.activity; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.graphics.Bitmap; 6 | import android.graphics.BitmapFactory; 7 | import android.support.v7.app.AppCompatActivity; 8 | import android.os.Bundle; 9 | import android.support.v7.widget.GridLayoutManager; 10 | import android.support.v7.widget.RecyclerView; 11 | import android.util.DisplayMetrics; 12 | import android.util.Log; 13 | import android.view.View; 14 | import android.view.animation.Animation; 15 | import android.view.animation.DecelerateInterpolator; 16 | import android.widget.ImageView; 17 | import android.widget.RelativeLayout; 18 | 19 | import com.orient.test.R; 20 | import com.orient.test.adapter.BookAdapter; 21 | import com.orient.test.animation.ContentScaleAnimation; 22 | import com.orient.test.animation.Rotate3DAnimation; 23 | 24 | import java.util.ArrayList; 25 | import java.util.List; 26 | 27 | public class OpenBookActivity extends AppCompatActivity implements Animation.AnimationListener,BookAdapter.OnBookClickListener { 28 | private static final String TAG = "OpenBookActivity"; 29 | 30 | private RecyclerView mRecyclerView; 31 | private BookAdapter mAdapter; 32 | // 资源文件列表 33 | private List values = new ArrayList<>(); 34 | // 记录View的位置 35 | private int[] location = new int[2]; 36 | // 内容页 37 | private ImageView mContent; 38 | // 封面 39 | private ImageView mFirst; 40 | // 缩放动画 41 | private ContentScaleAnimation scaleAnimation; 42 | // 3D旋转动画 43 | private Rotate3DAnimation threeDAnimation; 44 | // 状态栏的高度 45 | private int statusHeight; 46 | // 是否打开书籍 其实是是否离开当前界面,跳转到其他的界面 47 | private boolean isOpenBook = false; 48 | 49 | public static void show(Context context) { 50 | Intent intent = new Intent(context, OpenBookActivity.class); 51 | context.startActivity(intent); 52 | } 53 | 54 | @Override 55 | protected void onCreate(Bundle savedInstanceState) { 56 | super.onCreate(savedInstanceState); 57 | setContentView(R.layout.activity_open_book); 58 | 59 | initWidget(); 60 | } 61 | 62 | private void initWidget() { 63 | mRecyclerView = findViewById(R.id.recycle); 64 | mContent = findViewById(R.id.img_content); 65 | mFirst = findViewById(R.id.img_first); 66 | 67 | // 获取状态栏高度 68 | statusHeight = -1; 69 | //获取status_bar_height资源的ID 70 | int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android"); 71 | if (resourceId > 0) { 72 | //根据资源ID获取响应的尺寸值 73 | statusHeight = getResources().getDimensionPixelSize(resourceId); 74 | } 75 | 76 | initData(); 77 | mRecyclerView.setLayoutManager(new GridLayoutManager(this,2)); 78 | mAdapter = new BookAdapter(values,this); 79 | mRecyclerView.setAdapter(mAdapter); 80 | } 81 | 82 | // 重复添加数据 83 | private void initData() { 84 | for(int i = 0;i<10;i++){ 85 | values.add(R.drawable.preview); 86 | } 87 | } 88 | 89 | @Override 90 | protected void onRestart() { 91 | super.onRestart(); 92 | 93 | // 当界面重新进入的时候进行合书的动画 94 | if(isOpenBook) { 95 | scaleAnimation.reverse(); 96 | threeDAnimation.reverse(); 97 | mFirst.clearAnimation(); 98 | mFirst.startAnimation(threeDAnimation); 99 | mContent.clearAnimation(); 100 | mContent.startAnimation(scaleAnimation); 101 | } 102 | } 103 | 104 | @Override 105 | public void onAnimationStart(Animation animation) { 106 | 107 | } 108 | 109 | @Override 110 | public void onAnimationEnd(Animation animation) { 111 | if(scaleAnimation.hasEnded() && threeDAnimation.hasEnded()) { 112 | // 两个动画都结束的时候再处理后续操作 113 | if (!isOpenBook) { 114 | isOpenBook = true; 115 | BookSampleActivity.show(this); 116 | } else { 117 | isOpenBook = false; 118 | mFirst.clearAnimation(); 119 | mContent.clearAnimation(); 120 | mFirst.setVisibility(View.GONE); 121 | mContent.setVisibility(View.GONE); 122 | } 123 | } 124 | } 125 | 126 | @Override 127 | public void onAnimationRepeat(Animation animation) { 128 | 129 | } 130 | 131 | @Override 132 | public void onItemClick(int pos,View view) { 133 | mFirst.setVisibility(View.VISIBLE); 134 | mContent.setVisibility(View.VISIBLE); 135 | 136 | // 计算当前的位置坐标 137 | view.getLocationInWindow(location); 138 | int width = view.getWidth(); 139 | int height = view.getHeight(); 140 | 141 | // 两个ImageView设置大小和位置 142 | RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) mFirst.getLayoutParams(); 143 | params.leftMargin = location[0]; 144 | params.topMargin = location[1] - statusHeight; 145 | params.width = width; 146 | params.height = height; 147 | mFirst.setLayoutParams(params); 148 | mContent.setLayoutParams(params); 149 | 150 | //mContent = new ImageView(MainActivity.this); 151 | Bitmap contentBitmap = Bitmap.createBitmap(width,height, Bitmap.Config.ARGB_8888); 152 | contentBitmap.eraseColor(getResources().getColor(R.color.read_theme_yellow)); 153 | mContent.setImageBitmap(contentBitmap); 154 | 155 | // mCover = new ImageView(MainActivity.this); 156 | Bitmap coverBitmap = BitmapFactory.decodeResource(getResources(),values.get(pos)); 157 | mFirst.setImageBitmap(coverBitmap); 158 | 159 | initAnimation(view); 160 | Log.i(TAG,"left:"+mFirst.getLeft()+"top:"+mFirst.getTop()); 161 | 162 | mContent.clearAnimation(); 163 | mContent.startAnimation(scaleAnimation); 164 | mFirst.clearAnimation(); 165 | mFirst.startAnimation(threeDAnimation); 166 | } 167 | 168 | // 初始化动画 169 | private void initAnimation(View view) { 170 | float viewWidth = view.getWidth(); 171 | float viewHeight = view.getHeight(); 172 | 173 | DisplayMetrics displayMetrics = new DisplayMetrics(); 174 | getWindow().getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); 175 | float maxWidth = displayMetrics.widthPixels; 176 | float maxHeight = displayMetrics.heightPixels; 177 | float horScale = maxWidth / viewWidth; 178 | float verScale = maxHeight / viewHeight; 179 | float scale = horScale > verScale ? horScale : verScale; 180 | 181 | scaleAnimation = new ContentScaleAnimation(location[0], location[1], scale, false); 182 | scaleAnimation.setInterpolator(new DecelerateInterpolator()); //设置插值器 183 | scaleAnimation.setDuration(1000); 184 | scaleAnimation.setFillAfter(true); //动画停留在最后一帧 185 | scaleAnimation.setAnimationListener(OpenBookActivity.this); 186 | 187 | threeDAnimation = new Rotate3DAnimation(OpenBookActivity.this, -180, 0 188 | , location[0], location[1], scale, true); 189 | threeDAnimation.setDuration(1000); //设置动画时长 190 | threeDAnimation.setFillAfter(true); //保持旋转后效果 191 | threeDAnimation.setInterpolator(new DecelerateInterpolator()); 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /app/src/main/java/com/orient/test/utils/GlideUtils.java: -------------------------------------------------------------------------------- 1 | package com.orient.test.utils; 2 | 3 | import android.content.Context; 4 | import android.widget.ImageView; 5 | 6 | import com.bumptech.glide.Glide; 7 | import com.bumptech.glide.load.resource.drawable.GlideDrawable; 8 | import com.bumptech.glide.request.animation.GlideAnimation; 9 | import com.bumptech.glide.request.target.SimpleTarget; 10 | import com.orient.test.R; 11 | 12 | /** 13 | * Created by wangjie on 2019/2/12. 14 | */ 15 | 16 | public class GlideUtils { 17 | /* 18 | 加载图片 19 | */ 20 | public static void loadUrl(Context context, String path, final ImageView imageView){ 21 | Glide.with(context).load(path).placeholder(R.drawable.shape_item_empty).error(R.drawable.shape_item_empty).centerCrop() 22 | .into(new SimpleTarget() { 23 | @Override 24 | public void onResourceReady(GlideDrawable resource, GlideAnimation glideAnimation) { 25 | imageView.setImageDrawable(resource); 26 | } 27 | }); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/orient/test/utils/UiUtils.java: -------------------------------------------------------------------------------- 1 | package com.orient.test.utils; 2 | 3 | import android.content.Context; 4 | 5 | /** 6 | * 屏幕的工具类 7 | * 8 | * Created by wangjie on 2019/3/24. 9 | */ 10 | 11 | public class UiUtils { 12 | 13 | public static int dp2px(Context context, float dpValue) { 14 | float scale = context.getResources().getDisplayMetrics().density; 15 | return (int) (dpValue * scale + 0.5f); 16 | } 17 | 18 | public static int sp2px(Context context, float spValue) { 19 | float fontScale = context.getResources().getDisplayMetrics().scaledDensity; 20 | return (int) (spValue * fontScale + 0.5f); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/beauty.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mCyp/Test/57fe609c1432f609f2776e00d927bc4a981a9bf9/app/src/main/res/drawable/beauty.jpeg -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_constraint_add.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_constraint_like.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_constraint_top.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_constraint_write.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/preview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mCyp/Test/57fe609c1432f609f2776e00d927bc4a981a9bf9/app/src/main/res/drawable/preview.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_bottom_menu_green.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_item_empty.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_main_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/th.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mCyp/Test/57fe609c1432f609f2776e00d927bc4a981a9bf9/app/src/main/res/drawable/th.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable/theme_leather_bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mCyp/Test/57fe609c1432f609f2776e00d927bc4a981a9bf9/app/src/main/res/drawable/theme_leather_bg.jpg -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_book_sample.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 |