diff --git a/README.md b/README.md index ec5bbe0..32384e7 100644 --- a/README.md +++ b/README.md @@ -8,15 +8,15 @@ - 本库libs下有个**AndroidUtils.aar**的依赖包,是由[AndroidUtils](https://github.com/D-clock/AndroidUtils)代码编译生成; - 想要查看**AndroidUtils.aar**中的源代码,可以参考[这里](https://github.com/D-clock/Doc/blob/master/Android/%E4%B8%AA%E4%BA%BA%E6%94%B6%E8%97%8F/%E5%A6%82%E4%BD%95%E6%9F%A5%E7%9C%8Baar%E7%9A%84%E6%BA%90%E4%BB%A3%E7%A0%81.md) -## 最新更新(编辑于2016年06月08日) +## 最新更新(编辑于2016年08月29日) -- 兼容处理 Android M 版本的 Runtime Permission ; -- 优化 build.gradle ,提高导入 moudle 体验; +- 兼添加夜间模式的实现分析,详见文章 [知乎和简书的夜间模式实现套路](notes/知乎和简书的夜间模式实现套路.md) ## 归档文章 - [你需要知道的Android拍照适配问题](notes/你需要知道的Android拍照适配问题.md) - [深入理解Android中的Matrix](notes/深入理解Android中的Matrix.md) +- [知乎和简书的夜间模式实现套路](notes/知乎和简书的夜间模式实现套路.md) ## 找我 diff --git a/build.gradle b/build.gradle index 4f16e25..a330344 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.1.2' + classpath 'com.android.tools.build:gradle:2.1.3' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } @@ -17,7 +17,7 @@ android { defaultConfig { applicationId "com.clock.study" - minSdkVersion 9 + minSdkVersion 11 targetSdkVersion 23 versionCode 1 versionName "1.0" @@ -42,4 +42,6 @@ dependencies { compile(name: 'AndroidUtils', ext: 'aar') //compile project(":AndroidUtils") compile 'com.android.support:appcompat-v7:23.2.1' + compile 'com.android.support:recyclerview-v7:23.2.1' + compile 'com.android.support:cardview-v7:23.1.1' } \ No newline at end of file diff --git "a/notes/346円267円261円345円205円245円344円275円277円347円224円250円 RecyclerView.md" "b/notes/346円267円261円345円205円245円344円275円277円347円224円250円 RecyclerView.md" deleted file mode 100644 index 2712705..0000000 --- "a/notes/346円267円261円345円205円245円344円275円277円347円224円250円 RecyclerView.md" +++ /dev/null @@ -1,3 +0,0 @@ -# RecyclerView 使用大全 - -整理罗列想要写的知识点 \ No newline at end of file diff --git "a/notes/347円237円245円344円271円216円345円222円214円347円256円200円344円271円246円347円232円204円345円244円234円351円227円264円346円250円241円345円274円217円345円256円236円347円216円260円345円245円227円350円267円257円.md" "b/notes/347円237円245円344円271円216円345円222円214円347円256円200円344円271円246円347円232円204円345円244円234円351円227円264円346円250円241円345円274円217円345円256円236円347円216円260円345円245円227円350円267円257円.md" new file mode 100644 index 0000000..6c5ca48 --- /dev/null +++ "b/notes/347円237円245円344円271円216円345円222円214円347円256円200円344円271円246円347円232円204円345円244円234円351円227円264円346円250円241円345円274円217円345円256円236円347円216円260円345円245円227円350円267円257円.md" @@ -0,0 +1,353 @@ +# 知乎和简书的夜间模式实现套路 + +Hello,大家好,我是Clock。今天要写的这篇文章主题是关于夜间模式的实现套路。本来这篇文章是上周要写的,结果因为上周末有其他事情,所以拖到这个周末才完成。曾经和薇薇(钛媒体漂亮的程序媛)聊过夜间模式实现的问题,当时薇薇酱负责钛媒体客户端的重构工作,有个夜间模式功能在考虑要不要用 Android M 新加的夜间模式特性。凭借稍微有点点老司机的经验,我直接说了 NO。按照以往的套路,通常新出的功能都会有坑,或者向下兼容性的问题。自己弄弄 Demo 玩玩是可以的,但是引入企业开发还是谨慎点,说白了就是先等等,让大家把坑填完了再用。果然,Android M 发正式版的时候,预览版里面的夜间模式功能被暂时移除了(哈哈哈哈,机智如我,最新发布的 Android N 正式版已经有夜间模式了,大家可以去玩玩)。 + +## 前言 + +好了,回归正题,说回夜间模式。在网上看到很多童鞋都说用什么什么框架来实现这个功能,然后仔细去看一下各个推荐的框架,发现其实都是动态换肤的,动态换肤可比夜间模式要复杂多了,未免大材小用了。说实话,我一直没用什么好思路,虽然网上有童鞋提供了一种思路是通过 setTheme 然后再 recreate Activity 的方式,但是这样带来的问题是非常多的,看起来就相当不科学(为什么不科学,后文会说)。**于是,直接想到了去逆向分析那些夜间模式做得好的应用的源代码,学习他们的实现套路。所以,本文的实现思路来自于编写这些应用的夜间模式功能的童鞋,先在这里向他们表示感谢。**我的手机里面使用高频的应用不少,其中简书和知乎是属于夜间模式做得相当 nice 的。先给两个效果图大家对比感受下 + + +| 简书 | 知乎 | +| ----------------- | ------------------ | +| | | + +如果大家仔细观察,肯定会发现,知乎的切换效果更漂亮些,因为它有一个渐变的效果。那么它们的夜间模式到底是如何实现的呢?别急接着往下看,你也可以。 + +## 实现套路 + +这里先展示一下我的实现效果吧 + +| 简书实现效果 | 知乎实现效果 | +| ----------------- | ------------------ | +| | | + +此处分为两个部分,一部分是 xml 文件中要干的活,一部分是 Java 代码要实现的活,先说 xml 吧。 + +### XML 配置 + +首先,先写一套UI界面出来,上方左边是两个 TextView,右边是两个 CheckBox,下方是一个 RecyclerView ,实现很简单,这里我不贴代码了。 + + + +接着,在 styles 文件中添加两个 Theme,一个是日间主题,一个是夜间主题。它们的属性都是一样的,唯一区别在于颜色效果不同。 + +```xml + + + + + + + +``` + +需要注意的是,上面的 clockTextColor 和 clockBackground 是我自定义的 color 类型属性 + +```xml + + + + + +``` + +然后再到所有需要实现夜间模式功能的 xml 布局文件中,加入类似下面设置,比如我在 RecyclerView 的 Item 布局文件中做了如下设置 + + + +稍稍解释下其作用,如 TextView 里的 android:textColor="?attr/clockTextColor" 是让其字体颜色跟随所设置的 Theme。到这里,xml 需要做的配置全部完成,接下来是 Java 代码实现了。 + +### Java 代码实现 + +大家可以先看下面的实现代码,看不懂的童鞋可以边结合我代码下方实现思路解说。 + +```java + +package com.clock.study.activity; + +import ... + +/** + * 夜间模式实现方案 + * + * @author Clock + * @since 2016年08月11日 + */ +public class DayNightActivity extends AppCompatActivity implements CompoundButton.OnCheckedChangeListener { + + private final static String TAG = DayNightActivity.class.getSimpleName(); + /**用于将主题设置保存到SharePreferences的工具类**/ + private DayNightHelper mDayNightHelper; + + private RecyclerView mRecyclerView; + + private LinearLayout mHeaderLayout; + private List mLayoutList; + private List mTextViewList; + private List mCheckBoxList; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + supportRequestWindowFeature(Window.FEATURE_NO_TITLE); + initData(); + initTheme(); + setContentView(R.layout.activity_day_night); + initView(); + } + + private void initView() { + mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view); + RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false); + mRecyclerView.setLayoutManager(layoutManager); + mRecyclerView.setAdapter(new SimpleAuthorAdapter()); + + mHeaderLayout = (LinearLayout) findViewById(R.id.header_layout); + + mLayoutList = new ArrayList(); + mLayoutList.add((RelativeLayout) findViewById(R.id.jianshu_layout)); + mLayoutList.add((RelativeLayout) findViewById(R.id.zhihu_layout)); + + mTextViewList = new ArrayList(); + mTextViewList.add((TextView) findViewById(R.id.tv_jianshu)); + mTextViewList.add((TextView) findViewById(R.id.tv_zhihu)); + + mCheckBoxList = new ArrayList(); + CheckBox ckbJianshu = (CheckBox) findViewById(R.id.ckb_jianshu); + ckbJianshu.setOnCheckedChangeListener(this); + mCheckBoxList.add(ckbJianshu); + CheckBox ckbZhihu = (CheckBox) findViewById(R.id.ckb_zhihu); + ckbZhihu.setOnCheckedChangeListener(this); + mCheckBoxList.add(ckbZhihu); + + } + + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + int viewId = buttonView.getId(); + if (viewId == R.id.ckb_jianshu) { + changeThemeByJianShu(); + + } else if (viewId == R.id.ckb_zhihu) { + changeThemeByZhiHu(); + + } + } + + private void initData() { + mDayNightHelper = new DayNightHelper(this); + } + + private void initTheme() { + if (mDayNightHelper.isDay()) { + setTheme(R.style.DayTheme); + } else { + setTheme(R.style.NightTheme); + } + } + + /** + * 切换主题设置 + */ + private void toggleThemeSetting() { + if (mDayNightHelper.isDay()) { + mDayNightHelper.setMode(DayNight.NIGHT); + setTheme(R.style.NightTheme); + } else { + mDayNightHelper.setMode(DayNight.DAY); + setTheme(R.style.DayTheme); + } + } + + /** + * 使用简书的实现套路来切换夜间主题 + */ + private void changeThemeByJianShu() { + toggleThemeSetting(); + refreshUI(); + } + + /** + * 使用知乎的实现套路来切换夜间主题 + */ + private void changeThemeByZhiHu() { + showAnimation(); + toggleThemeSetting(); + refreshUI(); + } + + /** + * 刷新UI界面 + */ + private void refreshUI() { + TypedValue background = new TypedValue();//背景色 + TypedValue textColor = new TypedValue();//字体颜色 + Resources.Theme theme = getTheme(); + theme.resolveAttribute(R.attr.clockBackground, background, true); + theme.resolveAttribute(R.attr.clockTextColor, textColor, true); + + mHeaderLayout.setBackgroundResource(background.resourceId); + for (RelativeLayout layout : mLayoutList) { + layout.setBackgroundResource(background.resourceId); + } + for (CheckBox checkBox : mCheckBoxList) { + checkBox.setBackgroundResource(background.resourceId); + } + for (TextView textView : mTextViewList) { + textView.setBackgroundResource(background.resourceId); + } + + Resources resources = getResources(); + for (TextView textView : mTextViewList) { + textView.setTextColor(resources.getColor(textColor.resourceId)); + } + + int childCount = mRecyclerView.getChildCount(); + for (int childIndex = 0; childIndex < childCount; childIndex++) { + ViewGroup childView = (ViewGroup) mRecyclerView.getChildAt(childIndex); + childView.setBackgroundResource(background.resourceId); + View infoLayout = childView.findViewById(R.id.info_layout); + infoLayout.setBackgroundResource(background.resourceId); + TextView nickName = (TextView) childView.findViewById(R.id.tv_nickname); + nickName.setBackgroundResource(background.resourceId); + nickName.setTextColor(resources.getColor(textColor.resourceId)); + TextView motto = (TextView) childView.findViewById(R.id.tv_motto); + motto.setBackgroundResource(background.resourceId); + motto.setTextColor(resources.getColor(textColor.resourceId)); + } + + //让 RecyclerView 缓存在 Pool 中的 Item 失效 + //那么,如果是ListView,要怎么做呢?这里的思路是通过反射拿到 AbsListView 类中的 RecycleBin 对象,然后同样再用反射去调用 clear 方法 + Class recyclerViewClass = RecyclerView.class; + try { + Field declaredField = recyclerViewClass.getDeclaredField("mRecycler"); + declaredField.setAccessible(true); + Method declaredMethod = Class.forName(RecyclerView.Recycler.class.getName()).getDeclaredMethod("clear", (Class>[]) new Class[0]); + declaredMethod.setAccessible(true); + declaredMethod.invoke(declaredField.get(mRecyclerView), new Object[0]); + RecyclerView.RecycledViewPool recycledViewPool = mRecyclerView.getRecycledViewPool(); + recycledViewPool.clear(); + + } catch (NoSuchFieldException e) { + e.printStackTrace(); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + + refreshStatusBar(); + } + + /** + * 刷新 StatusBar + */ + private void refreshStatusBar() { + if (Build.VERSION.SDK_INT>= 21) { + TypedValue typedValue = new TypedValue(); + Resources.Theme theme = getTheme(); + theme.resolveAttribute(R.attr.colorPrimary, typedValue, true); + getWindow().setStatusBarColor(getResources().getColor(typedValue.resourceId)); + } + } + + /** + * 展示一个切换动画 + */ + private void showAnimation() { + final View decorView = getWindow().getDecorView(); + Bitmap cacheBitmap = getCacheBitmapFromView(decorView); + if (decorView instanceof ViewGroup && cacheBitmap != null) { + final View view = new View(this); + view.setBackgroundDrawable(new BitmapDrawable(getResources(), cacheBitmap)); + ViewGroup.LayoutParams layoutParam = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT); + ((ViewGroup) decorView).addView(view, layoutParam); + ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(view, "alpha", 1f, 0f); + objectAnimator.setDuration(300); + objectAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + ((ViewGroup) decorView).removeView(view); + } + }); + objectAnimator.start(); + } + } + + /** + * 获取一个 View 的缓存视图 + * + * @param view + * @return + */ + private Bitmap getCacheBitmapFromView(View view) { + final boolean drawingCacheEnabled = true; + view.setDrawingCacheEnabled(drawingCacheEnabled); + view.buildDrawingCache(drawingCacheEnabled); + final Bitmap drawingCache = view.getDrawingCache(); + Bitmap bitmap; + if (drawingCache != null) { + bitmap = Bitmap.createBitmap(drawingCache); + view.setDrawingCacheEnabled(false); + } else { + bitmap = null; + } + return bitmap; + } +} + + +``` + +实现思路和代码解说: + +1. DayNightHelper 类是用于保存夜间模式设置到 SharePreferences 的工具类,在 initData 函数中被初始化,其他的 View 和 Layout 都是界面布局,在 initView 函数中被初始化; +2. 在 Activity 的 onCreate 函数调用 setContentView 之前,需要先去 setTheme,因为当 View 创建成功后 ,再去 setTheme 是无法对 View 的 UI 效果产生影响的; +3. onCheckedChanged 用于监听日间模式和夜间模式的切换操作; +4. **refreshUI 是本实现的关键函数**,起着切换效果的作用,通过 TypedValue 和 Theme.resolveAttribute 在代码中获取 Theme 中设置的颜色,来重新设置控件的背景色或者字体颜色等等。**需要特别注意的是 RecyclerView 和 ListView 这种比较特殊的控件处理方式,代码注释中已经说明,大家可以看代码中注释**; +5. refreshStatusBar 用于刷新顶部通知栏位置的颜色; +6. **showAnimation 和 getCacheBitmapFromView 同样是本实现的关键函数**,getCacheBitmapFromView 用于将 View 中的内容转换成 Bitmap(类似于截屏操作那样),showAnimation 是用于展示一个渐隐效果的属性动画,这个属性作用在哪个对象上呢?是一个 View ,一个在代码中动态填充到 DecorView 中的 View(不知道 DecorView 的童鞋得回去看看 Android Window 相关的知识)。**知乎之所以在夜间模式切换过程中会有渐隐效果,是因为在切换前进行了截屏,同时将截屏拿到的 Bitmap 设置到动态填充到 DecorView 中的 View 上,并对这个 View 执行一个渐隐的属性动画,所以使得我们能够看到一个漂亮的渐隐过渡的动画效果。而且在动画结束的时候再把这个动态添加的 View 给 remove 了,避免了 Bitmap 造成内存飙升问题。**对待知乎客户端开发者这种处理方式,我必须双手点赞外加一个大写的服。 + +到这里,实现套路基本说完了,简书和知乎的实现套路如上所述,区别就是知乎多了个截屏和渐隐过渡动画效果而已。 + +## 一些思考 + +整理逆向分析的过程,也对夜间模式的实现有了不少思考,希望与各位童鞋们探讨分享。 + +> 最初步的逆向分析过程就发现了,知乎和简书并没有引入任何第三方框架来实现夜间模式,为什么呢? + +因为我看到的大部分都实现夜间模式的思路都是用开源的换肤框架,或多或少存在着些 BUG。简书和知乎不用可能是出于框架不稳定性,以及我前面提到的用换肤框架来实现夜间模式大材小用吧。(我也只是瞎猜,哈哈哈) + +> 前面我提到,通过 setTheme 然后再去 Activity recreate 的方案不可行,为什么呢? + +我认为不可行的原因有两点,一个是 Activity recreate 会有闪烁效果体验不加,二是 Activity recreate 涉及到状态状态保存问题,如自身的状态保存,如果 Activity 中包含着多个 Fragment ,那就更加头疼了。 + +> 知乎和简书设置夜间模式的位置,有点巧妙,巧妙在哪? + +知乎和简书出发夜间模式切换的地方,都是在 MainActivity 的一个 Fragment 中。也就是说,如果你要切换模式时,必须回到主界面,此时只存在主界面一个 Activity,只需要遍历主界面更新控件色调即可。而对于其他设置夜间模式后新建的 Activity ,只需要在 setContentView 之前做一下判断并 setTheme 即可。 + +## 总结 + +关于简书和知乎夜间模式功能实现的套路就讲解到这里,**整个实现套路都是我通过逆向分析简书和知乎的代码取得,这里再一次向实现这些代码的童鞋以示感谢。**当然,上面的代码我是经过精简提炼过的,在原先简书和知乎客户端中的实现代码还做了相应的抽象设计和递归遍历等等,这里是为了方便讲解而做了精简。如果有童鞋喜欢这种实现套路,也可以自己加以抽象封装。这里也推荐各位童鞋一个我常用的思路,就是当你对一个功能没有思路时,大可找一些实现了这类功能的优秀应用进行逆向代码分析。需要实现代码的童鞋,可以访问: + +> https://github.com/D-clock/AndroidStudyCode + +**也欢迎大家关注我的[简书](http://www.jianshu.com/users/ec95b5891948/latest_articles)和[Github](https://github.com/D-clock)。** \ No newline at end of file diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index 7a0cbc0..5417e19 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -29,8 +29,21 @@ android:name=".activity.PhotoPreviewActivity" android:label="@string/photo_preview" android:screenOrientation="portrait" /> - - + + + + + \ No newline at end of file diff --git a/src/main/java/com/clock/study/activity/AnimationTestActivity.java b/src/main/java/com/clock/study/activity/AnimationTestActivity.java index 187e5ed..24c2eac 100644 --- a/src/main/java/com/clock/study/activity/AnimationTestActivity.java +++ b/src/main/java/com/clock/study/activity/AnimationTestActivity.java @@ -59,7 +59,7 @@ public long getItemId(int position) { @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { - convertView = View.inflate(parent.getContext(), R.layout.list_view_test_item, null); + convertView = View.inflate(parent.getContext(), R.layout.author_info_layout, null); } return convertView; } diff --git a/src/main/java/com/clock/study/activity/AnimatorActivity.java b/src/main/java/com/clock/study/activity/AnimatorActivity.java new file mode 100644 index 0000000..91d4622 --- /dev/null +++ b/src/main/java/com/clock/study/activity/AnimatorActivity.java @@ -0,0 +1,266 @@ +package com.clock.study.activity; + +import android.animation.AnimatorInflater; +import android.animation.AnimatorSet; +import android.animation.Keyframe; +import android.animation.ObjectAnimator; +import android.animation.PropertyValuesHolder; +import android.animation.ValueAnimator; +import android.graphics.Color; +import android.os.Bundle; +import android.support.v4.view.ViewCompat; +import android.support.v7.app.AppCompatActivity; +import android.util.Log; +import android.view.View; +import android.view.animation.BounceInterpolator; + +import com.clock.study.R; + +/** + * About Android Animator + * + * @author Clock + * @since 2016年07月21日 + */ +public class AnimatorActivity extends AppCompatActivity implements View.OnClickListener { + + private static final String TAG = AnimatorActivity.class.getSimpleName(); + + private View mTarget; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_animator); + + mTarget = findViewById(R.id.anim_target); + + findViewById(R.id.btn_value_anim).setOnClickListener(this); + findViewById(R.id.btn_object_anim_alpha).setOnClickListener(this); + findViewById(R.id.btn_object_anim_rotation).setOnClickListener(this); + findViewById(R.id.btn_object_anim_set).setOnClickListener(this); + findViewById(R.id.btn_object_anim_xml).setOnClickListener(this); + findViewById(R.id.btn_simple_value_animator).setOnClickListener(this); + findViewById(R.id.btn_value_animator_argb).setOnClickListener(this); + findViewById(R.id.btn_bounce_interpolator).setOnClickListener(this); + findViewById(R.id.btn_simple_key_frame).setOnClickListener(this); + findViewById(R.id.btn_shake_key_frame).setOnClickListener(this); + + } + + @Override + public void onClick(View v) { + int viewId = v.getId(); + if (viewId == R.id.btn_value_anim) { + ValueAnimator valueAnimator = ValueAnimator.ofInt(1, 100); + valueAnimator.setDuration(3000); + valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + int currentValue = (int) animation.getAnimatedValue(); + Log.i(TAG, "currentValue: " + currentValue); + } + }); + valueAnimator.start(); + + } else if (viewId == R.id.btn_object_anim_alpha) { + ObjectAnimator alphaObjectAnimator = ObjectAnimator.ofFloat(mTarget, "alpha", 1f, 0f, 1f); + alphaObjectAnimator.setDuration(3000); + alphaObjectAnimator.start(); + + } else if (viewId == R.id.btn_object_anim_rotation) { + ObjectAnimator rotationObjectAnimator = ObjectAnimator.ofFloat(mTarget, "rotation", 0f, 360f); + rotationObjectAnimator.setDuration(3000); + rotationObjectAnimator.start(); + + } else if (viewId == R.id.btn_object_anim_set) { + AnimatorSet animatorSet = new AnimatorSet(); + ObjectAnimator alphaObjectAnimator = ObjectAnimator.ofFloat(mTarget, "alpha", 1f, 0f, 1f); + ObjectAnimator rotationObjectAnimator = ObjectAnimator.ofFloat(mTarget, "rotation", 0f, 360f); + animatorSet.play(alphaObjectAnimator).with(rotationObjectAnimator); + //animatorSet.play(alphaObjectAnimator).after(rotationObjectAnimator); + //animatorSet.playTogether(alphaObjectAnimator, rotationObjectAnimator); + animatorSet.setDuration(3000); + animatorSet.start(); + + } else if (viewId == R.id.btn_object_anim_xml) { + AnimatorSet animatorSet = (AnimatorSet) AnimatorInflater.loadAnimator(this, R.animator.simple_animator); + animatorSet.setTarget(mTarget); + animatorSet.start(); + + } else if (viewId == R.id.btn_simple_value_animator) { + displayColorAnimation(mTarget, "#0000ff", "#ff0000"); + + } else if (viewId == R.id.btn_value_animator_argb) { + if (android.os.Build.VERSION.SDK_INT>= android.os.Build.VERSION_CODES.LOLLIPOP) { + int startColor = 0x00000000; + int centerColor = 0xff00ff89; + int endColor = 0x00000000; + ValueAnimator valueAnimator = ValueAnimator.ofArgb(startColor, centerColor, endColor); + valueAnimator.setDuration(6000); + valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + int color = (int) animation.getAnimatedValue(); + mTarget.setBackgroundColor(color); + } + }); + valueAnimator.start(); + } + + } else if (viewId == R.id.btn_bounce_interpolator) { + ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mTarget, "translationY", 0f, 1500f); + objectAnimator.setInterpolator(new BounceInterpolator()); + objectAnimator.setDuration(4000); + objectAnimator.start(); + + } else if (viewId == R.id.btn_simple_key_frame) { + Keyframe kf0 = Keyframe.ofFloat(0f, 0f); + Keyframe kf1 = Keyframe.ofFloat(.5f, 360f); + Keyframe kf2 = Keyframe.ofFloat(1f, 0f); + PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2); + ObjectAnimator rotationAnim = ObjectAnimator.ofPropertyValuesHolder(mTarget, pvhRotation); + rotationAnim.setDuration(5000); + rotationAnim.start(); + + } else if (viewId == R.id.btn_shake_key_frame) { + displayShakeAnimator(mTarget, 1f); + + } + } + + /** + * 创建一个旋转动画 + * + * @param target + * @param shakeFactor + * @return + */ + private void displayShakeAnimator(View target, float shakeFactor) { + Keyframe scaleXkf0 = Keyframe.ofFloat(0f, 1f); + Keyframe scaleXkf1 = Keyframe.ofFloat(0.1f, 0.9f); + Keyframe scaleXkf2 = Keyframe.ofFloat(0.2f, 0.9f); + Keyframe scaleXkf3 = Keyframe.ofFloat(0.3f, 0.9f); + Keyframe scaleXkf4 = Keyframe.ofFloat(0.4f, 1.1f); + Keyframe scaleXkf5 = Keyframe.ofFloat(0.5f, 1.1f); + Keyframe scaleXkf6 = Keyframe.ofFloat(0.6f, 1.1f); + Keyframe scaleXkf7 = Keyframe.ofFloat(0.7f, 1.1f); + Keyframe scaleXkf8 = Keyframe.ofFloat(0.8f, 1.1f); + Keyframe scaleXkf9 = Keyframe.ofFloat(0.9f, 1.1f); + Keyframe scaleXkf10 = Keyframe.ofFloat(1f, 1f); + + PropertyValuesHolder scaleXHolder = PropertyValuesHolder.ofKeyframe("scaleX", scaleXkf0, scaleXkf1, scaleXkf2, scaleXkf3, scaleXkf4, + scaleXkf5, scaleXkf6, scaleXkf7, scaleXkf8, scaleXkf9, scaleXkf10); + + Keyframe scaleYkf0 = Keyframe.ofFloat(0f, 1f); + Keyframe scaleYkf1 = Keyframe.ofFloat(0.1f, 0.9f); + Keyframe scaleYkf2 = Keyframe.ofFloat(0.2f, 0.9f); + Keyframe scaleYkf3 = Keyframe.ofFloat(0.3f, 0.9f); + Keyframe scaleYkf4 = Keyframe.ofFloat(0.4f, 1.1f); + Keyframe scaleYkf5 = Keyframe.ofFloat(0.5f, 1.1f); + Keyframe scaleYkf6 = Keyframe.ofFloat(0.6f, 1.1f); + Keyframe scaleYkf7 = Keyframe.ofFloat(0.7f, 1.1f); + Keyframe scaleYkf8 = Keyframe.ofFloat(0.8f, 1.1f); + Keyframe scaleYkf9 = Keyframe.ofFloat(0.9f, 1.1f); + Keyframe scaleYkf10 = Keyframe.ofFloat(1f, 1f); + PropertyValuesHolder scaleYHolder = PropertyValuesHolder.ofKeyframe("scaleY", scaleYkf0, scaleYkf1, scaleYkf2, scaleYkf3, scaleYkf4, + scaleYkf5, scaleYkf6, scaleYkf7, scaleYkf8, scaleYkf9, scaleYkf10); + + + PropertyValuesHolder rotationHolder = PropertyValuesHolder.ofKeyframe("rotation", + Keyframe.ofFloat(0f, 0), + Keyframe.ofFloat(0.1f, -3 * shakeFactor), + Keyframe.ofFloat(0.2f, -3 * shakeFactor), + Keyframe.ofFloat(0.3f, 3 * shakeFactor), + Keyframe.ofFloat(0.4f, -3 * shakeFactor), + Keyframe.ofFloat(0.5f, 3 * shakeFactor), + Keyframe.ofFloat(0.6f, -3 * shakeFactor), + Keyframe.ofFloat(0.7f, -3 * shakeFactor), + Keyframe.ofFloat(0.8f, 3 * shakeFactor), + Keyframe.ofFloat(0.9f, -3 * shakeFactor), + Keyframe.ofFloat(1f, 0)); + + + ObjectAnimator.ofPropertyValuesHolder(target, scaleXHolder, scaleYHolder, rotationHolder).setDuration(1000).start(); + } + + /** + * 显示颜色变化的动画 + * + * @param target + * @param startColor + * @param endColor + */ + private void displayColorAnimation(final View target, final String startColor, final String endColor) { + ValueAnimator valueAnimator = ValueAnimator.ofFloat(0f, 100f); + valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + if (android.os.Build.VERSION.SDK_INT>= android.os.Build.VERSION_CODES.HONEYCOMB_MR1) { + float fraction = animation.getAnimatedFraction(); + if (target != null) { + int startRed = Integer.parseInt(startColor.substring(1, 3), 16); + int startGreen = Integer.parseInt(startColor.substring(3, 5), 16); + int startBlue = Integer.parseInt(startColor.substring(5, 7), 16); + int endRed = Integer.parseInt(endColor.substring(1, 3), 16); + int endGreen = Integer.parseInt(endColor.substring(3, 5), 16); + int endBlue = Integer.parseInt(endColor.substring(5, 7), 16); + + int redDiff = Math.abs(endRed - startRed); + int greenDiff = Math.abs(endGreen - startGreen); + int blueDiff = Math.abs(endBlue - startBlue); + int colorDiff = redDiff + greenDiff + blueDiff; + + int currRed = getCurrentColor(startRed, endRed, colorDiff, fraction); + int currGreen = getCurrentColor(startGreen, endGreen, colorDiff, fraction); + int currBlue = getCurrentColor(startBlue, endBlue, colorDiff, fraction); + + String colorString = "#" + getHexString(currRed) + getHexString(currGreen) + getHexString(currBlue); + int color = Color.parseColor(colorString); + target.setBackgroundColor(color); + + } + } + + } + }); + valueAnimator.setDuration(3000); + valueAnimator.start(); + + } + + /** + * 获取当前新颜色 + * + * @param startColor + * @param endColor + * @param colorDiff + * @param fraction + * @return + */ + private int getCurrentColor(int startColor, int endColor, int colorDiff, float fraction) { + int currentColor = 0; + if (startColor> endColor) { + currentColor = (int) (startColor - fraction * colorDiff); + } else { + currentColor = (int) (startColor + fraction * colorDiff); + } + if (currentColor>= 0 && currentColor <= 256) {//最终的色值要确保在0到256之间 + return currentColor; + } else { + currentColor = endColor; + } + return currentColor; + } + + /** + * 将10进制颜色值转换成16进制。 + */ + private String getHexString(int value) { + String hexString = Integer.toHexString(value); + if (hexString.length() == 1) { + hexString = "0" + hexString; + } + return hexString; + } +} diff --git a/src/main/java/com/clock/study/activity/AuthorActivity.java b/src/main/java/com/clock/study/activity/AuthorActivity.java new file mode 100644 index 0000000..9d47ff6 --- /dev/null +++ b/src/main/java/com/clock/study/activity/AuthorActivity.java @@ -0,0 +1,32 @@ +package com.clock.study.activity; + +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; + +import com.clock.study.R; +import com.clock.study.helper.DayNightHelper; + +public class AuthorActivity extends AppCompatActivity { + + private DayNightHelper mDayNightHelper; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + initData(); + initTheme(); + setContentView(R.layout.activity_author); + } + + private void initData() { + mDayNightHelper = new DayNightHelper(this); + } + + private void initTheme() { + if (mDayNightHelper.isDay()) { + setTheme(R.style.DayTheme); + } else { + setTheme(R.style.NightTheme); + } + } +} diff --git a/src/main/java/com/clock/study/activity/DayNightActivity.java b/src/main/java/com/clock/study/activity/DayNightActivity.java new file mode 100644 index 0000000..396b427 --- /dev/null +++ b/src/main/java/com/clock/study/activity/DayNightActivity.java @@ -0,0 +1,269 @@ +package com.clock.study.activity; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.os.Build; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.util.TypedValue; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import com.clock.study.R; +import com.clock.study.adapter.SimpleAuthorAdapter; +import com.clock.study.helper.DayNightHelper; +import com.clock.study.type.DayNight; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +/** + * 夜间模式实现方案 + * + * @author Clock + * @since 2016-08-11 + */ +public class DayNightActivity extends AppCompatActivity implements CompoundButton.OnCheckedChangeListener { + + private final static String TAG = DayNightActivity.class.getSimpleName(); + + private DayNightHelper mDayNightHelper; + + private RecyclerView mRecyclerView; + + private LinearLayout mHeaderLayout; + private List mLayoutList; + private List mTextViewList; + private List mCheckBoxList; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + supportRequestWindowFeature(Window.FEATURE_NO_TITLE); + initData(); + initTheme(); + setContentView(R.layout.activity_day_night); + initView(); + } + + private void initView() { + mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view); + RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false); + mRecyclerView.setLayoutManager(layoutManager); + mRecyclerView.setAdapter(new SimpleAuthorAdapter()); + + mHeaderLayout = (LinearLayout) findViewById(R.id.header_layout); + + mLayoutList = new ArrayList(); + mLayoutList.add((RelativeLayout) findViewById(R.id.jianshu_layout)); + mLayoutList.add((RelativeLayout) findViewById(R.id.zhihu_layout)); + + mTextViewList = new ArrayList(); + mTextViewList.add((TextView) findViewById(R.id.tv_jianshu)); + mTextViewList.add((TextView) findViewById(R.id.tv_zhihu)); + + mCheckBoxList = new ArrayList(); + CheckBox ckbJianshu = (CheckBox) findViewById(R.id.ckb_jianshu); + ckbJianshu.setOnCheckedChangeListener(this); + mCheckBoxList.add(ckbJianshu); + CheckBox ckbZhihu = (CheckBox) findViewById(R.id.ckb_zhihu); + ckbZhihu.setOnCheckedChangeListener(this); + mCheckBoxList.add(ckbZhihu); + + } + + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + int viewId = buttonView.getId(); + if (viewId == R.id.ckb_jianshu) { + changeThemeByJianShu(); + + } else if (viewId == R.id.ckb_zhihu) { + changeThemeByZhiHu(); + + } + } + + private void initData() { + mDayNightHelper = new DayNightHelper(this); + } + + private void initTheme() { + if (mDayNightHelper.isDay()) { + setTheme(R.style.DayTheme); + } else { + setTheme(R.style.NightTheme); + } + } + + /** + * 切换主题设置 + */ + private void toggleThemeSetting() { + if (mDayNightHelper.isDay()) { + mDayNightHelper.setMode(DayNight.NIGHT); + setTheme(R.style.NightTheme); + } else { + mDayNightHelper.setMode(DayNight.DAY); + setTheme(R.style.DayTheme); + } + } + + /** + * 使用简书的实现套路来切换夜间主题 + */ + private void changeThemeByJianShu() { + toggleThemeSetting(); + refreshUI(); + } + + /** + * 使用知乎的实现套路来切换夜间主题 + */ + private void changeThemeByZhiHu() { + showAnimation(); + toggleThemeSetting(); + refreshUI(); + } + + /** + * 刷新UI界面 + */ + private void refreshUI() { + TypedValue background = new TypedValue();//背景色 + TypedValue textColor = new TypedValue();//字体颜色 + Resources.Theme theme = getTheme(); + theme.resolveAttribute(R.attr.clockBackground, background, true); + theme.resolveAttribute(R.attr.clockTextColor, textColor, true); + + mHeaderLayout.setBackgroundResource(background.resourceId); + for (RelativeLayout layout : mLayoutList) { + layout.setBackgroundResource(background.resourceId); + } + for (CheckBox checkBox : mCheckBoxList) { + checkBox.setBackgroundResource(background.resourceId); + } + for (TextView textView : mTextViewList) { + textView.setBackgroundResource(background.resourceId); + } + + Resources resources = getResources(); + for (TextView textView : mTextViewList) { + textView.setTextColor(resources.getColor(textColor.resourceId)); + } + + int childCount = mRecyclerView.getChildCount(); + for (int childIndex = 0; childIndex < childCount; childIndex++) { + ViewGroup childView = (ViewGroup) mRecyclerView.getChildAt(childIndex); + childView.setBackgroundResource(background.resourceId); + View infoLayout = childView.findViewById(R.id.info_layout); + infoLayout.setBackgroundResource(background.resourceId); + TextView nickName = (TextView) childView.findViewById(R.id.tv_nickname); + nickName.setBackgroundResource(background.resourceId); + nickName.setTextColor(resources.getColor(textColor.resourceId)); + TextView motto = (TextView) childView.findViewById(R.id.tv_motto); + motto.setBackgroundResource(background.resourceId); + motto.setTextColor(resources.getColor(textColor.resourceId)); + } + + //让 RecyclerView 缓存在 Pool 中的 Item 失效 + //那么,如果是ListView,要怎么做呢?这里的思路是通过反射拿到 AbsListView 类中的 RecycleBin 对象,然后同样再用反射去调用 clear 方法 + Class recyclerViewClass = RecyclerView.class; + try { + Field declaredField = recyclerViewClass.getDeclaredField("mRecycler"); + declaredField.setAccessible(true); + Method declaredMethod = Class.forName(RecyclerView.Recycler.class.getName()).getDeclaredMethod("clear", (Class>[]) new Class[0]); + declaredMethod.setAccessible(true); + declaredMethod.invoke(declaredField.get(mRecyclerView), new Object[0]); + RecyclerView.RecycledViewPool recycledViewPool = mRecyclerView.getRecycledViewPool(); + recycledViewPool.clear(); + + } catch (NoSuchFieldException e) { + e.printStackTrace(); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + + refreshStatusBar(); + } + + /** + * 刷新 StatusBar + */ + private void refreshStatusBar() { + if (Build.VERSION.SDK_INT>= 21) { + TypedValue typedValue = new TypedValue(); + Resources.Theme theme = getTheme(); + theme.resolveAttribute(R.attr.colorPrimary, typedValue, true); + getWindow().setStatusBarColor(getResources().getColor(typedValue.resourceId)); + } + } + + /** + * 展示一个切换动画 + */ + private void showAnimation() { + final View decorView = getWindow().getDecorView(); + Bitmap cacheBitmap = getCacheBitmapFromView(decorView); + if (decorView instanceof ViewGroup && cacheBitmap != null) { + final View view = new View(this); + view.setBackgroundDrawable(new BitmapDrawable(getResources(), cacheBitmap)); + ViewGroup.LayoutParams layoutParam = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT); + ((ViewGroup) decorView).addView(view, layoutParam); + ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(view, "alpha", 1f, 0f); + objectAnimator.setDuration(300); + objectAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + ((ViewGroup) decorView).removeView(view); + } + }); + objectAnimator.start(); + } + } + + /** + * 获取一个 View 的缓存视图 + * + * @param view + * @return + */ + private Bitmap getCacheBitmapFromView(View view) { + final boolean drawingCacheEnabled = true; + view.setDrawingCacheEnabled(drawingCacheEnabled); + view.buildDrawingCache(drawingCacheEnabled); + final Bitmap drawingCache = view.getDrawingCache(); + Bitmap bitmap; + if (drawingCache != null) { + bitmap = Bitmap.createBitmap(drawingCache); + view.setDrawingCacheEnabled(false); + } else { + bitmap = null; + } + return bitmap; + } +} diff --git a/src/main/java/com/clock/study/activity/MainActivity.java b/src/main/java/com/clock/study/activity/MainActivity.java index b1d1da5..4b0b1f8 100644 --- a/src/main/java/com/clock/study/activity/MainActivity.java +++ b/src/main/java/com/clock/study/activity/MainActivity.java @@ -16,6 +16,9 @@ protected void onCreate(Bundle savedInstanceState) { findViewById(R.id.btn_camera_take_photo).setOnClickListener(this); findViewById(R.id.btn_animation).setOnClickListener(this); + findViewById(R.id.btn_animator).setOnClickListener(this); + findViewById(R.id.btn_day_night).setOnClickListener(this); + } @Override @@ -25,8 +28,14 @@ public void onClick(View v) { Intent takePhotoIntent = new Intent(this, CapturePhotoActivity.class); startActivity(takePhotoIntent); } else if (viewId == R.id.btn_animation) { - Intent androidAnimIntent = new Intent(this, AnimationActivity.class); - startActivity(androidAnimIntent); + Intent animationIntent = new Intent(this, AnimationActivity.class); + startActivity(animationIntent); + } else if (viewId == R.id.btn_animator) { + Intent animatorIntent = new Intent(this, AnimatorActivity.class); + startActivity(animatorIntent); + } else if (viewId == R.id.btn_day_night) { + Intent animatorIntent = new Intent(this, DayNightActivity.class); + startActivity(animatorIntent); } } } diff --git a/src/main/java/com/clock/study/adapter/SimpleAuthorAdapter.java b/src/main/java/com/clock/study/adapter/SimpleAuthorAdapter.java new file mode 100644 index 0000000..f8f2d5b --- /dev/null +++ b/src/main/java/com/clock/study/adapter/SimpleAuthorAdapter.java @@ -0,0 +1,51 @@ +package com.clock.study.adapter; + +import android.content.Context; +import android.content.Intent; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.clock.study.R; +import com.clock.study.activity.AuthorActivity; + +/** + * Just Simple Author Adapter + * + * Created by Clock on 2016年8月24日. + */ +public class SimpleAuthorAdapter extends RecyclerView.Adapter { + + private final View.OnClickListener mSimpleClickListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + Context context = v.getContext(); + Intent intent = new Intent(context, AuthorActivity.class); + context.startActivity(intent); + } + }; + + @Override + public SimpleViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + LayoutInflater inflater = LayoutInflater.from(parent.getContext()); + View rootView = inflater.inflate(R.layout.author_info_layout, parent, false); + return new SimpleViewHolder(rootView); + } + + @Override + public void onBindViewHolder(SimpleViewHolder holder, int position) { + holder.itemView.setOnClickListener(mSimpleClickListener); + } + + @Override + public int getItemCount() { + return 20; + } + + static class SimpleViewHolder extends RecyclerView.ViewHolder { + public SimpleViewHolder(View itemView) { + super(itemView); + } + } +} diff --git a/src/main/java/com/clock/study/animator/ColorEvaluator.java b/src/main/java/com/clock/study/animator/ColorEvaluator.java new file mode 100644 index 0000000..22e1328 --- /dev/null +++ b/src/main/java/com/clock/study/animator/ColorEvaluator.java @@ -0,0 +1,31 @@ +package com.clock.study.animator; + +import android.animation.TypeEvaluator; + +/** + * 仿照了 ArgbEvaluator 的系统实现,用于产生颜色过度渐变效果 + * + * Created by Clock on 2016年7月22日. + */ +public class ColorEvaluator implements TypeEvaluator { + + @Override + public Object evaluate(float fraction, Object startValue, Object endValue) { + int startInt = (Integer) startValue; + int startA = (startInt>> 24) & 0xff; + int startR = (startInt>> 16) & 0xff; + int startG = (startInt>> 8) & 0xff; + int startB = startInt & 0xff; + + int endInt = (Integer) endValue; + int endA = (endInt>> 24) & 0xff; + int endR = (endInt>> 16) & 0xff; + int endG = (endInt>> 8) & 0xff; + int endB = endInt & 0xff; + + return (int) ((startA + (int) (fraction * (endA - startA))) << 24) | + (int) ((startR + (int) (fraction * (endR - startR))) << 16) | + (int) ((startG + (int) (fraction * (endG - startG))) << 8) | + (int) ((startB + (int) (fraction * (endB - startB)))); + } +} diff --git a/src/main/java/com/clock/study/helper/DayNightHelper.java b/src/main/java/com/clock/study/helper/DayNightHelper.java new file mode 100644 index 0000000..602cb72 --- /dev/null +++ b/src/main/java/com/clock/study/helper/DayNightHelper.java @@ -0,0 +1,61 @@ +package com.clock.study.helper; + +import android.content.Context; +import android.content.SharedPreferences; + +import com.clock.study.type.DayNight; + +/** + * Created by Clock on 2016/8/24. + */ +public class DayNightHelper { + + private final static String FILE_NAME = "settings"; + private final static String MODE = "day_night_mode"; + + private SharedPreferences mSharedPreferences; + + public DayNightHelper(Context context) { + this.mSharedPreferences = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE); + } + + /** + * 保存模式设置 + * + * @param mode + * @return + */ + public boolean setMode(DayNight mode) { + SharedPreferences.Editor editor = mSharedPreferences.edit(); + editor.putString(MODE, mode.getName()); + return editor.commit(); + } + + /** + * 夜间模式 + * + * @return + */ + public boolean isNight() { + String mode = mSharedPreferences.getString(MODE, DayNight.DAY.getName()); + if (DayNight.NIGHT.getName().equals(mode)) { + return true; + } else { + return false; + } + } + + /** + * 日间模式 + * + * @return + */ + public boolean isDay() { + String mode = mSharedPreferences.getString(MODE, DayNight.DAY.getName()); + if (DayNight.DAY.getName().equals(mode)) { + return true; + } else { + return false; + } + } +} diff --git a/src/main/java/com/clock/study/type/DayNight.java b/src/main/java/com/clock/study/type/DayNight.java new file mode 100644 index 0000000..15e338e --- /dev/null +++ b/src/main/java/com/clock/study/type/DayNight.java @@ -0,0 +1,34 @@ +package com.clock.study.type; + +/** + * Created by Clock on 2016/8/24. + */ +public enum DayNight { + + DAY("DAY", 0), + NIGHT("NIGHT", 1); + + private String name; + private int code; + + private DayNight(String name, int code) { + this.name = name; + this.code = code; + } + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/src/main/res/animator/simple_animator.xml b/src/main/res/animator/simple_animator.xml new file mode 100644 index 0000000..87a4808 --- /dev/null +++ b/src/main/res/animator/simple_animator.xml @@ -0,0 +1,18 @@ + + + + + + + \ No newline at end of file diff --git a/src/main/res/layout/activity_animator.xml b/src/main/res/layout/activity_animator.xml new file mode 100644 index 0000000..4788b01 --- /dev/null +++ b/src/main/res/layout/activity_animator.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/res/layout/activity_author.xml b/src/main/res/layout/activity_author.xml new file mode 100644 index 0000000..d4a4423 --- /dev/null +++ b/src/main/res/layout/activity_author.xml @@ -0,0 +1,29 @@ + + + + + + + + diff --git a/src/main/res/layout/activity_day_night.xml b/src/main/res/layout/activity_day_night.xml new file mode 100644 index 0000000..5658082 --- /dev/null +++ b/src/main/res/layout/activity_day_night.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/res/layout/activity_main.xml b/src/main/res/layout/activity_main.xml index c061c3d..f87e9e4 100644 --- a/src/main/res/layout/activity_main.xml +++ b/src/main/res/layout/activity_main.xml @@ -18,5 +18,18 @@ android:id="@+id/btn_animation" android:layout_width="match_parent" android:layout_height="wrap_content" - android:text="Android Animation" /> + android:text="@string/android_animation" /> + + + + + diff --git a/src/main/res/layout/author_card_layout.xml b/src/main/res/layout/author_card_layout.xml new file mode 100644 index 0000000..96e8f5e --- /dev/null +++ b/src/main/res/layout/author_card_layout.xml @@ -0,0 +1,20 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/main/res/layout/list_view_test_item.xml b/src/main/res/layout/author_info_layout.xml similarity index 69% rename from src/main/res/layout/list_view_test_item.xml rename to src/main/res/layout/author_info_layout.xml index 78b3558..e0c8d63 100644 --- a/src/main/res/layout/list_view_test_item.xml +++ b/src/main/res/layout/author_info_layout.xml @@ -3,6 +3,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="80dp" + android:background="?attr/clockBackground" android:orientation="horizontal"> + android:textStyle="bold" /> + android:background="?attr/clockBackground" + android:text="Diycode 大管家,闭关写代码" + android:textColor="?attr/clockTextColor" /> \ No newline at end of file diff --git a/src/main/res/values/attrs.xml b/src/main/res/values/attrs.xml new file mode 100644 index 0000000..887e1b9 --- /dev/null +++ b/src/main/res/values/attrs.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/main/res/values/colors.xml b/src/main/res/values/colors.xml index 3ab3e9c..ea98acc 100644 --- a/src/main/res/values/colors.xml +++ b/src/main/res/values/colors.xml @@ -3,4 +3,8 @@ #3F51B5 #303F9F #FF4081 + #3A3A3A + #3F3F3F + #8A9599 + #868686 diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index de587c7..96cc839 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -9,4 +9,8 @@ 当前权限被禁用,建议到设置界面开启权限! 取消 设置 + Android Animation + Android Animator + DayNight Mode + Animation Test diff --git a/src/main/res/values/styles.xml b/src/main/res/values/styles.xml index 5885930..4b7d43b 100644 --- a/src/main/res/values/styles.xml +++ b/src/main/res/values/styles.xml @@ -6,6 +6,26 @@ @color/colorPrimary @color/colorPrimaryDark @color/colorAccent + @android:color/white + @android:color/black + + + + + + + diff --git a/src/test/java/com/clock/study/ExampleUnitTest.java b/src/test/java/com/clock/study/ExampleUnitTest.java index 310fb93..36e3ec5 100644 --- a/src/test/java/com/clock/study/ExampleUnitTest.java +++ b/src/test/java/com/clock/study/ExampleUnitTest.java @@ -5,7 +5,7 @@ import static org.junit.Assert.*; /** - * To work on unit tests, switch the Test Artifact in the Build Variants view. + * To work on unit tests, switch the ColorEvaluator Artifact in the Build Variants view. */ public class ExampleUnitTest { @Test AltStyle によって変換されたページ (->オリジナル) / アドレス: モード: デフォルト 音声ブラウザ ルビ付き 配色反転 文字拡大 モバイル
AltStyle によって変換されたページ (->オリジナル) / アドレス: モード: デフォルト 音声ブラウザ ルビ付き 配色反転 文字拡大 モバイル