├── README.md └── Timeline ├── .gitignore ├── .idea ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── gradle.xml ├── inspectionProfiles │ ├── Project_Default.xml │ └── profiles_settings.xml ├── misc.xml ├── modules.xml └── runConfigurations.xml ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── lvr │ │ └── timeline │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── lvr │ │ │ └── timeline │ │ │ ├── adapter │ │ │ └── TimelineAdapter.java │ │ │ ├── anims │ │ │ ├── AlphaInAnimationAdapter.java │ │ │ ├── AnimationAdapter.java │ │ │ ├── BaseItemAnimator.java │ │ │ ├── LandingAnimator.java │ │ │ ├── ScaleInAnimationAdapter.java │ │ │ └── ViewHelper.java │ │ │ ├── app │ │ │ ├── AppApplication.java │ │ │ ├── AppConstantValue.java │ │ │ └── AppManager.java │ │ │ ├── base │ │ │ ├── BaseActivity.java │ │ │ ├── BaseApplication.java │ │ │ ├── BaseFragment.java │ │ │ └── BaseFragmentAdapter.java │ │ │ ├── bean │ │ │ └── TimeInfo.java │ │ │ ├── home │ │ │ ├── fragment │ │ │ │ ├── CountDownFragment.java │ │ │ │ ├── TodayFragment.java │ │ │ │ └── YesterdayFragment.java │ │ │ └── ui │ │ │ │ ├── EditActivity.java │ │ │ │ └── MainActivity.java │ │ │ ├── utils │ │ │ ├── ACache.java │ │ │ ├── CollectionUtils.java │ │ │ ├── DisplayUtil.java │ │ │ ├── GlideRoundTransformUtil.java │ │ │ ├── ImageLoaderUtils.java │ │ │ ├── RealmOperationHelper.java │ │ │ ├── StatusBarSetting.java │ │ │ ├── TUtil.java │ │ │ └── TimeUtil.java │ │ │ └── widget │ │ │ ├── CircleImageView.java │ │ │ ├── CircleTextView.java │ │ │ ├── ItemDragHelperCallback.java │ │ │ └── StatusBarView.java │ └── res │ │ ├── drawable-xxhdpi │ │ ├── about.png │ │ ├── calender.png │ │ ├── change.png │ │ ├── fb_plus.png │ │ ├── habbit.png │ │ ├── ic_empty_picture.png │ │ ├── ic_image_loading.png │ │ ├── ic_search_menu.png │ │ ├── item_delete.png │ │ ├── mn_back.png │ │ ├── mn_edit.png │ │ ├── mn_right.png │ │ ├── moddy.png │ │ ├── nav_header.jpg │ │ ├── nav_photo.jpg │ │ ├── out.png │ │ ├── progress.png │ │ ├── select_time_online.png │ │ ├── select_today.png │ │ ├── select_yesterday.png │ │ ├── sorry_hint.png │ │ ├── thing.png │ │ ├── time_online.png │ │ ├── today.png │ │ ├── toux2.png │ │ └── yesterday.png │ │ ├── drawable │ │ ├── select_button_countdown.xml │ │ ├── select_button_today.xml │ │ └── select_button_yesterday.xml │ │ ├── layout │ │ ├── activity_edit.xml │ │ ├── activity_home.xml │ │ ├── app_bar.xml │ │ ├── dialog_view.xml │ │ ├── fragment_countdown.xml │ │ ├── fragment_today.xml │ │ ├── fragment_yesterday.xml │ │ ├── item_line.xml │ │ └── view_nav.xml │ │ ├── menu │ │ ├── menu_nav.xml │ │ └── menu_toolabr.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── time.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ ├── dimens.xml │ │ └── ids.xml │ │ └── values │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── lvr │ └── timeline │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /README.md: -------------------------------------------------------------------------------- 1 | # TimeLine 2 | 一款时间管理软件,可以记录每天需要完成的事项,并进行监督执行。同时可以对每天的时间进行管理分析。 3 | -------------------------------------------------------------------------------- /Timeline/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /Timeline/.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Timeline/.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /Timeline/.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Timeline/.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /Timeline/.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | -------------------------------------------------------------------------------- /Timeline/.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /Timeline/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | 47 | 48 | 49 | 50 | 1.8 51 | 52 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /Timeline/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Timeline/.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /Timeline/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /Timeline/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'com.neenbedankt.android-apt' 3 | apply plugin: 'realm-android' 4 | android { 5 | compileSdkVersion 25 6 | buildToolsVersion "25.0.2" 7 | defaultConfig { 8 | applicationId "com.lvr.timeline" 9 | minSdkVersion 15 10 | targetSdkVersion 25 11 | versionCode 1 12 | versionName "1.0" 13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 14 | } 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | } 22 | 23 | dependencies { 24 | compile fileTree(include: ['*.jar'], dir: 'libs') 25 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 26 | exclude group: 'com.android.support', module: 'support-annotations' 27 | }) 28 | compile 'com.android.support:appcompat-v7:25.1.0' 29 | testCompile 'junit:junit:4.12' 30 | compile 'com.android.support:design:25.1.0' 31 | compile 'com.android.support:cardview-v7:25.1.0' 32 | compile 'com.android.support:recyclerview-v7:25.1.0' 33 | //视图绑定 butterknife 34 | compile 'com.jakewharton:butterknife:8.4.0' 35 | apt 'com.jakewharton:butterknife-compiler:8.4.0' 36 | compile 'com.github.bumptech.glide:glide:3.7.0' 37 | compile 'de.greenrobot:eventbus:3.0.0-beta1' 38 | compile 'com.borax12.materialdaterangepicker:library:1.9' 39 | //侧滑删除Item 40 | compile 'com.github.mcxtzhang:SwipeDelMenuLayout:V1.1.0' 41 | } 42 | -------------------------------------------------------------------------------- /Timeline/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in E:\AndroidStudio\SDKinstallpackage/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /Timeline/app/src/androidTest/java/com/lvr/timeline/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.lvr.timeline; 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 | * Instrumentation 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() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.lvr.timeline", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Timeline/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Timeline/app/src/main/java/com/lvr/timeline/adapter/TimelineAdapter.java: -------------------------------------------------------------------------------- 1 | package com.lvr.timeline.adapter; 2 | 3 | import android.content.Context; 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.Button; 9 | import android.widget.ImageButton; 10 | import android.widget.RelativeLayout; 11 | import android.widget.TextView; 12 | import android.widget.Toast; 13 | 14 | import com.lvr.timeline.R; 15 | import com.lvr.timeline.app.AppApplication; 16 | import com.lvr.timeline.bean.TimeInfo; 17 | import com.lvr.timeline.utils.RealmOperationHelper; 18 | import com.mcxtzhang.swipemenulib.SwipeMenuLayout; 19 | 20 | import java.text.SimpleDateFormat; 21 | import java.util.Date; 22 | import java.util.List; 23 | 24 | 25 | public class TimelineAdapter extends RecyclerView.Adapter { 26 | private List list; 27 | private Context context; 28 | private OnItemClickListener mOnItemClickListener; 29 | private int[] colors; 30 | public TimelineAdapter(Context context, List list) { 31 | this.list=list; 32 | this.context=context; 33 | this.colors = new int[]{context.getResources().getColor(R.color.color1), context.getResources().getColor(R.color.color2), context.getResources().getColor(R.color.color3) 34 | , context.getResources().getColor(R.color.color4), context.getResources().getColor(R.color.color5), context.getResources().getColor(R.color.color6), context.getResources().getColor(R.color.color7) 35 | , context.getResources().getColor(R.color.color8), context.getResources().getColor(R.color.color9), context.getResources().getColor(R.color.color10), context.getResources().getColor(R.color.color11) 36 | , context.getResources().getColor(R.color.color12), context.getResources().getColor(R.color.color13), context.getResources().getColor(R.color.color14), context.getResources().getColor(R.color.color15) 37 | , context.getResources().getColor(R.color.color16), context.getResources().getColor(R.color.color17), context.getResources().getColor(R.color.color18), context.getResources().getColor(R.color.color19) 38 | , context.getResources().getColor(R.color.color20), context.getResources().getColor(R.color.color21), context.getResources().getColor(R.color.color22), context.getResources().getColor(R.color.color23) 39 | , context.getResources().getColor(R.color.color24)}; 40 | 41 | } 42 | @Override 43 | public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 44 | View v = LayoutInflater.from(parent.getContext()) 45 | .inflate(R.layout.item_line, parent,false); 46 | return new ViewHolder(v); 47 | } 48 | 49 | @Override 50 | public void onBindViewHolder(final ViewHolder holder, final int position) { 51 | TimeInfo info = list.get(position); 52 | holder.yMDText.setText(info.getYmD()); 53 | holder.titleText.setText(info.getTitle()); 54 | holder.contentText.setText(info.getContent()); 55 | // TODO: 2017/4/17 有问题 创建该条目的时间会出错 56 | SimpleDateFormat format = new SimpleDateFormat("HH:mm"); 57 | String allTime = format.format(new Date()); 58 | holder.timeText.setText(allTime); 59 | String[] split = info.getAddTime().split("-"); 60 | holder.hourText.setText(split[0]); 61 | String[] hours = split[0].split(":"); 62 | System.out.println(hours); 63 | int hour = Integer.parseInt(hours[0]); 64 | System.out.println(hour); 65 | int color = colors[hour-1]; 66 | holder.hourText.setBackgroundColor(color); 67 | holder.hourText.setTextColor(context.getResources().getColor(R.color.white)); 68 | holder.rl_root.setOnClickListener(new View.OnClickListener() { 69 | @Override 70 | public void onClick(View v) { 71 | mOnItemClickListener.onItemClick(list.get(position),position); 72 | } 73 | }); 74 | 75 | ((SwipeMenuLayout) holder.itemView).setIos(false).setLeftSwipe(true); 76 | holder.deleteIButton.setOnClickListener(new View.OnClickListener() { 77 | @Override 78 | public void onClick(View v) { 79 | int curPosition = holder.getPosition(); 80 | System.out.println("当前位置:"+curPosition); 81 | list.remove(curPosition); 82 | notifyItemRemoved(curPosition); 83 | RealmOperationHelper.getInstance(AppApplication.REALM_INSTANCE).deleteElement(TimeInfo.class,curPosition); 84 | 85 | } 86 | }); 87 | holder.completeButton.setOnClickListener(new View.OnClickListener() { 88 | @Override 89 | public void onClick(View v) { 90 | Toast.makeText(context,"完成了",Toast.LENGTH_SHORT).show(); 91 | } 92 | }); 93 | } 94 | 95 | @Override 96 | public int getItemCount() { 97 | return list.size(); 98 | } 99 | 100 | 101 | 102 | 103 | public static class ViewHolder extends RecyclerView.ViewHolder { 104 | private TextView hourText; 105 | private RelativeLayout rl_root; 106 | private TextView yMDText; 107 | private TextView titleText; 108 | private TextView timeText; 109 | private TextView contentText; 110 | private ImageButton deleteIButton; 111 | private Button completeButton; 112 | public ViewHolder(View v) { 113 | super(v); 114 | titleText = (TextView) v.findViewById(R.id.tv_title); 115 | yMDText = (TextView) v.findViewById(R.id.tv_yMD); 116 | timeText = (TextView) v.findViewById(R.id.tv_time); 117 | contentText = (TextView) v.findViewById(R.id.tv_content); 118 | rl_root = (RelativeLayout) v.findViewById(R.id.rl_root); 119 | hourText = (TextView) v.findViewById(R.id.tv_hour); 120 | deleteIButton = (ImageButton) v.findViewById(R.id.ib_delete); 121 | completeButton = (Button) v.findViewById(R.id.btn_complete); 122 | } 123 | } 124 | public interface OnItemClickListener{ 125 | void onItemClick(TimeInfo mInfo,int position); 126 | } 127 | public void setOnItemClickListener(OnItemClickListener onItemClickListener){ 128 | this.mOnItemClickListener = onItemClickListener; 129 | } 130 | 131 | 132 | } 133 | -------------------------------------------------------------------------------- /Timeline/app/src/main/java/com/lvr/timeline/anims/AlphaInAnimationAdapter.java: -------------------------------------------------------------------------------- 1 | package com.lvr.timeline.anims; 2 | 3 | import android.animation.Animator; 4 | import android.animation.ObjectAnimator; 5 | import android.support.v7.widget.RecyclerView; 6 | import android.view.View; 7 | 8 | /** 9 | * Copyright (C) 2017 Wasabeef 10 | * 11 | * Licensed under the Apache License, Version 2.0 (the "License"); 12 | * you may not use this file except in compliance with the License. 13 | * You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, software 18 | * distributed under the License is distributed on an "AS IS" BASIS, 19 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | * See the License for the specific language governing permissions and 21 | * limitations under the License. 22 | */ 23 | 24 | public class AlphaInAnimationAdapter extends AnimationAdapter { 25 | 26 | private static final float DEFAULT_ALPHA_FROM = 0f; 27 | private final float mFrom; 28 | 29 | public AlphaInAnimationAdapter(RecyclerView.Adapter adapter) { 30 | this(adapter, DEFAULT_ALPHA_FROM); 31 | } 32 | 33 | public AlphaInAnimationAdapter(RecyclerView.Adapter adapter, float from) { 34 | super(adapter); 35 | mFrom = from; 36 | } 37 | 38 | @Override protected Animator[] getAnimators(View view) { 39 | return new Animator[] { ObjectAnimator.ofFloat(view, "alpha", mFrom, 1f) }; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Timeline/app/src/main/java/com/lvr/timeline/anims/AnimationAdapter.java: -------------------------------------------------------------------------------- 1 | package com.lvr.timeline.anims; 2 | 3 | import android.animation.Animator; 4 | import android.support.v7.widget.RecyclerView; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.view.animation.Interpolator; 8 | import android.view.animation.LinearInterpolator; 9 | 10 | /** 11 | * Copyright (C) 2017 Wasabeef 12 | * 13 | * Licensed under the Apache License, Version 2.0 (the "License"); 14 | * you may not use this file except in compliance with the License. 15 | * You may obtain a copy of the License at 16 | * 17 | * http://www.apache.org/licenses/LICENSE-2.0 18 | * 19 | * Unless required by applicable law or agreed to in writing, software 20 | * distributed under the License is distributed on an "AS IS" BASIS, 21 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 22 | * See the License for the specific language governing permissions and 23 | * limitations under the License. 24 | */ 25 | public abstract class AnimationAdapter extends RecyclerView.Adapter { 26 | 27 | private RecyclerView.Adapter mAdapter; 28 | private int mDuration = 300; 29 | private Interpolator mInterpolator = new LinearInterpolator(); 30 | private int mLastPosition = -1; 31 | 32 | private boolean isFirstOnly = true; 33 | 34 | public AnimationAdapter(RecyclerView.Adapter adapter) { 35 | mAdapter = adapter; 36 | } 37 | 38 | @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 39 | return mAdapter.onCreateViewHolder(parent, viewType); 40 | } 41 | 42 | @Override public void registerAdapterDataObserver(RecyclerView.AdapterDataObserver observer) { 43 | super.registerAdapterDataObserver(observer); 44 | mAdapter.registerAdapterDataObserver(observer); 45 | } 46 | 47 | @Override public void unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver observer) { 48 | super.unregisterAdapterDataObserver(observer); 49 | mAdapter.unregisterAdapterDataObserver(observer); 50 | } 51 | 52 | @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { 53 | mAdapter.onBindViewHolder(holder, position); 54 | 55 | int adapterPosition = holder.getAdapterPosition(); 56 | if (!isFirstOnly || adapterPosition > mLastPosition) { 57 | for (Animator anim : getAnimators(holder.itemView)) { 58 | anim.setDuration(mDuration).start(); 59 | anim.setInterpolator(mInterpolator); 60 | } 61 | mLastPosition = adapterPosition; 62 | } else { 63 | ViewHelper.clear(holder.itemView); 64 | } 65 | } 66 | 67 | @Override public void onViewRecycled(RecyclerView.ViewHolder holder) { 68 | mAdapter.onViewRecycled(holder); 69 | super.onViewRecycled(holder); 70 | } 71 | 72 | @Override public int getItemCount() { 73 | return mAdapter.getItemCount(); 74 | } 75 | 76 | public void setDuration(int duration) { 77 | mDuration = duration; 78 | } 79 | 80 | public void setInterpolator(Interpolator interpolator) { 81 | mInterpolator = interpolator; 82 | } 83 | 84 | public void setStartPosition(int start) { 85 | mLastPosition = start; 86 | } 87 | 88 | protected abstract Animator[] getAnimators(View view); 89 | 90 | public void setFirstOnly(boolean firstOnly) { 91 | isFirstOnly = firstOnly; 92 | } 93 | 94 | @Override public int getItemViewType(int position) { 95 | return mAdapter.getItemViewType(position); 96 | } 97 | 98 | public RecyclerView.Adapter getWrappedAdapter() { 99 | return mAdapter; 100 | } 101 | 102 | @Override public long getItemId(int position) { 103 | return mAdapter.getItemId(position); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /Timeline/app/src/main/java/com/lvr/timeline/anims/BaseItemAnimator.java: -------------------------------------------------------------------------------- 1 | package com.lvr.timeline.anims; 2 | /* 3 | * Copyright (C) 2017 Wasabeef 4 | * Copyright (C) 2014 The Android Open Source Project 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | import android.support.v4.view.ViewCompat; 21 | import android.support.v4.view.ViewPropertyAnimatorCompat; 22 | import android.support.v4.view.ViewPropertyAnimatorListener; 23 | import android.support.v7.widget.RecyclerView.ViewHolder; 24 | import android.support.v7.widget.SimpleItemAnimator; 25 | import android.view.View; 26 | import android.view.animation.DecelerateInterpolator; 27 | import android.view.animation.Interpolator; 28 | 29 | import java.util.ArrayList; 30 | import java.util.List; 31 | 32 | public abstract class BaseItemAnimator extends SimpleItemAnimator { 33 | 34 | private static final boolean DEBUG = false; 35 | 36 | private ArrayList mPendingRemovals = new ArrayList<>(); 37 | private ArrayList mPendingAdditions = new ArrayList<>(); 38 | private ArrayList mPendingMoves = new ArrayList<>(); 39 | private ArrayList mPendingChanges = new ArrayList<>(); 40 | 41 | private ArrayList> mAdditionsList = new ArrayList<>(); 42 | private ArrayList> mMovesList = new ArrayList<>(); 43 | private ArrayList> mChangesList = new ArrayList<>(); 44 | 45 | protected ArrayList mAddAnimations = new ArrayList<>(); 46 | private ArrayList mMoveAnimations = new ArrayList<>(); 47 | protected ArrayList mRemoveAnimations = new ArrayList<>(); 48 | private ArrayList mChangeAnimations = new ArrayList<>(); 49 | 50 | protected Interpolator mInterpolator = new DecelerateInterpolator(); 51 | 52 | private static class MoveInfo { 53 | 54 | public ViewHolder holder; 55 | public int fromX, fromY, toX, toY; 56 | 57 | private MoveInfo(ViewHolder holder, int fromX, int fromY, int toX, int toY) { 58 | this.holder = holder; 59 | this.fromX = fromX; 60 | this.fromY = fromY; 61 | this.toX = toX; 62 | this.toY = toY; 63 | } 64 | } 65 | 66 | private static class ChangeInfo { 67 | 68 | public ViewHolder oldHolder, newHolder; 69 | public int fromX, fromY, toX, toY; 70 | 71 | private ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder) { 72 | this.oldHolder = oldHolder; 73 | this.newHolder = newHolder; 74 | } 75 | 76 | private ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder, int fromX, int fromY, int toX, 77 | int toY) { 78 | this(oldHolder, newHolder); 79 | this.fromX = fromX; 80 | this.fromY = fromY; 81 | this.toX = toX; 82 | this.toY = toY; 83 | } 84 | 85 | @Override public String toString() { 86 | return "ChangeInfo{" + 87 | "oldHolder=" + oldHolder + 88 | ", newHolder=" + newHolder + 89 | ", fromX=" + fromX + 90 | ", fromY=" + fromY + 91 | ", toX=" + toX + 92 | ", toY=" + toY + 93 | '}'; 94 | } 95 | } 96 | 97 | public BaseItemAnimator() { 98 | super(); 99 | setSupportsChangeAnimations(false); 100 | } 101 | 102 | public void setInterpolator(Interpolator mInterpolator) { 103 | this.mInterpolator = mInterpolator; 104 | } 105 | 106 | @Override public void runPendingAnimations() { 107 | boolean removalsPending = !mPendingRemovals.isEmpty(); 108 | boolean movesPending = !mPendingMoves.isEmpty(); 109 | boolean changesPending = !mPendingChanges.isEmpty(); 110 | boolean additionsPending = !mPendingAdditions.isEmpty(); 111 | if (!removalsPending && !movesPending && !additionsPending && !changesPending) { 112 | // nothing to animate 113 | return; 114 | } 115 | // First, remove stuff 116 | for (ViewHolder holder : mPendingRemovals) { 117 | doAnimateRemove(holder); 118 | } 119 | mPendingRemovals.clear(); 120 | // Next, move stuff 121 | if (movesPending) { 122 | final ArrayList moves = new ArrayList(); 123 | moves.addAll(mPendingMoves); 124 | mMovesList.add(moves); 125 | mPendingMoves.clear(); 126 | Runnable mover = new Runnable() { 127 | @Override public void run() { 128 | boolean removed = mMovesList.remove(moves); 129 | if (!removed) { 130 | // already canceled 131 | return; 132 | } 133 | for (MoveInfo moveInfo : moves) { 134 | animateMoveImpl(moveInfo.holder, moveInfo.fromX, moveInfo.fromY, moveInfo.toX, 135 | moveInfo.toY); 136 | } 137 | moves.clear(); 138 | } 139 | }; 140 | if (removalsPending) { 141 | View view = moves.get(0).holder.itemView; 142 | ViewCompat.postOnAnimationDelayed(view, mover, getRemoveDuration()); 143 | } else { 144 | mover.run(); 145 | } 146 | } 147 | // Next, change stuff, to run in parallel with move animations 148 | if (changesPending) { 149 | final ArrayList changes = new ArrayList(); 150 | changes.addAll(mPendingChanges); 151 | mChangesList.add(changes); 152 | mPendingChanges.clear(); 153 | Runnable changer = new Runnable() { 154 | @Override public void run() { 155 | boolean removed = mChangesList.remove(changes); 156 | if (!removed) { 157 | // already canceled 158 | return; 159 | } 160 | for (ChangeInfo change : changes) { 161 | animateChangeImpl(change); 162 | } 163 | changes.clear(); 164 | } 165 | }; 166 | if (removalsPending) { 167 | ViewHolder holder = changes.get(0).oldHolder; 168 | ViewCompat.postOnAnimationDelayed(holder.itemView, changer, getRemoveDuration()); 169 | } else { 170 | changer.run(); 171 | } 172 | } 173 | // Next, add stuff 174 | if (additionsPending) { 175 | final ArrayList additions = new ArrayList(); 176 | additions.addAll(mPendingAdditions); 177 | mAdditionsList.add(additions); 178 | mPendingAdditions.clear(); 179 | Runnable adder = new Runnable() { 180 | public void run() { 181 | boolean removed = mAdditionsList.remove(additions); 182 | if (!removed) { 183 | // already canceled 184 | return; 185 | } 186 | for (ViewHolder holder : additions) { 187 | doAnimateAdd(holder); 188 | } 189 | additions.clear(); 190 | } 191 | }; 192 | if (removalsPending || movesPending || changesPending) { 193 | long removeDuration = removalsPending ? getRemoveDuration() : 0; 194 | long moveDuration = movesPending ? getMoveDuration() : 0; 195 | long changeDuration = changesPending ? getChangeDuration() : 0; 196 | long totalDelay = removeDuration + Math.max(moveDuration, changeDuration); 197 | View view = additions.get(0).itemView; 198 | ViewCompat.postOnAnimationDelayed(view, adder, totalDelay); 199 | } else { 200 | adder.run(); 201 | } 202 | } 203 | } 204 | 205 | protected void preAnimateRemoveImpl(final ViewHolder holder) { 206 | } 207 | 208 | protected void preAnimateAddImpl(final ViewHolder holder) { 209 | } 210 | 211 | protected abstract void animateRemoveImpl(final ViewHolder holder); 212 | 213 | protected abstract void animateAddImpl(final ViewHolder holder); 214 | 215 | private void preAnimateRemove(final ViewHolder holder) { 216 | ViewHelper.clear(holder.itemView); 217 | 218 | if (holder instanceof AnimateViewHolder) { 219 | ((AnimateViewHolder) holder).preAnimateRemoveImpl(holder); 220 | } else { 221 | preAnimateRemoveImpl(holder); 222 | } 223 | } 224 | 225 | private void preAnimateAdd(final ViewHolder holder) { 226 | ViewHelper.clear(holder.itemView); 227 | 228 | if (holder instanceof AnimateViewHolder) { 229 | ((AnimateViewHolder) holder).preAnimateAddImpl(holder); 230 | } else { 231 | preAnimateAddImpl(holder); 232 | } 233 | } 234 | 235 | private void doAnimateRemove(final ViewHolder holder) { 236 | if (holder instanceof AnimateViewHolder) { 237 | ((AnimateViewHolder) holder).animateRemoveImpl(holder, new DefaultRemoveVpaListener(holder)); 238 | } else { 239 | animateRemoveImpl(holder); 240 | } 241 | 242 | mRemoveAnimations.add(holder); 243 | } 244 | 245 | private void doAnimateAdd(final ViewHolder holder) { 246 | if (holder instanceof AnimateViewHolder) { 247 | ((AnimateViewHolder) holder).animateAddImpl(holder, new DefaultAddVpaListener(holder)); 248 | } else { 249 | animateAddImpl(holder); 250 | } 251 | 252 | mAddAnimations.add(holder); 253 | } 254 | 255 | @Override public boolean animateRemove(final ViewHolder holder) { 256 | endAnimation(holder); 257 | preAnimateRemove(holder); 258 | mPendingRemovals.add(holder); 259 | return true; 260 | } 261 | 262 | protected long getRemoveDelay(final ViewHolder holder) { 263 | return Math.abs(holder.getOldPosition() * getRemoveDuration() / 4); 264 | } 265 | 266 | @Override public boolean animateAdd(final ViewHolder holder) { 267 | endAnimation(holder); 268 | preAnimateAdd(holder); 269 | mPendingAdditions.add(holder); 270 | return true; 271 | } 272 | 273 | protected long getAddDelay(final ViewHolder holder) { 274 | return Math.abs(holder.getAdapterPosition() * getAddDuration() / 4); 275 | } 276 | 277 | @Override 278 | public boolean animateMove(final ViewHolder holder, int fromX, int fromY, int toX, int toY) { 279 | final View view = holder.itemView; 280 | fromX += ViewCompat.getTranslationX(holder.itemView); 281 | fromY += ViewCompat.getTranslationY(holder.itemView); 282 | endAnimation(holder); 283 | int deltaX = toX - fromX; 284 | int deltaY = toY - fromY; 285 | if (deltaX == 0 && deltaY == 0) { 286 | dispatchMoveFinished(holder); 287 | return false; 288 | } 289 | if (deltaX != 0) { 290 | ViewCompat.setTranslationX(view, -deltaX); 291 | } 292 | if (deltaY != 0) { 293 | ViewCompat.setTranslationY(view, -deltaY); 294 | } 295 | mPendingMoves.add(new MoveInfo(holder, fromX, fromY, toX, toY)); 296 | return true; 297 | } 298 | 299 | private void animateMoveImpl(final ViewHolder holder, int fromX, int fromY, int toX, int toY) { 300 | final View view = holder.itemView; 301 | final int deltaX = toX - fromX; 302 | final int deltaY = toY - fromY; 303 | if (deltaX != 0) { 304 | ViewCompat.animate(view).translationX(0); 305 | } 306 | if (deltaY != 0) { 307 | ViewCompat.animate(view).translationY(0); 308 | } 309 | // TODO: make EndActions end listeners instead, since end actions aren't called when 310 | // vpas are canceled (and can't end them. why?) 311 | // need listener functionality in VPACompat for this. Ick. 312 | mMoveAnimations.add(holder); 313 | final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view); 314 | animation.setDuration(getMoveDuration()).setListener(new VpaListenerAdapter() { 315 | @Override public void onAnimationStart(View view) { 316 | dispatchMoveStarting(holder); 317 | } 318 | 319 | @Override public void onAnimationCancel(View view) { 320 | if (deltaX != 0) { 321 | ViewCompat.setTranslationX(view, 0); 322 | } 323 | if (deltaY != 0) { 324 | ViewCompat.setTranslationY(view, 0); 325 | } 326 | } 327 | 328 | @Override public void onAnimationEnd(View view) { 329 | animation.setListener(null); 330 | dispatchMoveFinished(holder); 331 | mMoveAnimations.remove(holder); 332 | dispatchFinishedWhenDone(); 333 | } 334 | }).start(); 335 | } 336 | 337 | @Override 338 | public boolean animateChange(ViewHolder oldHolder, ViewHolder newHolder, int fromX, int fromY, 339 | int toX, int toY) { 340 | final float prevTranslationX = ViewCompat.getTranslationX(oldHolder.itemView); 341 | final float prevTranslationY = ViewCompat.getTranslationY(oldHolder.itemView); 342 | final float prevAlpha = ViewCompat.getAlpha(oldHolder.itemView); 343 | endAnimation(oldHolder); 344 | int deltaX = (int) (toX - fromX - prevTranslationX); 345 | int deltaY = (int) (toY - fromY - prevTranslationY); 346 | // recover prev translation state after ending animation 347 | ViewCompat.setTranslationX(oldHolder.itemView, prevTranslationX); 348 | ViewCompat.setTranslationY(oldHolder.itemView, prevTranslationY); 349 | ViewCompat.setAlpha(oldHolder.itemView, prevAlpha); 350 | if (newHolder != null && newHolder.itemView != null) { 351 | // carry over translation values 352 | endAnimation(newHolder); 353 | ViewCompat.setTranslationX(newHolder.itemView, -deltaX); 354 | ViewCompat.setTranslationY(newHolder.itemView, -deltaY); 355 | ViewCompat.setAlpha(newHolder.itemView, 0); 356 | } 357 | mPendingChanges.add(new ChangeInfo(oldHolder, newHolder, fromX, fromY, toX, toY)); 358 | return true; 359 | } 360 | 361 | private void animateChangeImpl(final ChangeInfo changeInfo) { 362 | final ViewHolder holder = changeInfo.oldHolder; 363 | final View view = holder == null ? null : holder.itemView; 364 | final ViewHolder newHolder = changeInfo.newHolder; 365 | final View newView = newHolder != null ? newHolder.itemView : null; 366 | if (view != null) { 367 | mChangeAnimations.add(changeInfo.oldHolder); 368 | final ViewPropertyAnimatorCompat oldViewAnim = 369 | ViewCompat.animate(view).setDuration(getChangeDuration()); 370 | oldViewAnim.translationX(changeInfo.toX - changeInfo.fromX); 371 | oldViewAnim.translationY(changeInfo.toY - changeInfo.fromY); 372 | oldViewAnim.alpha(0).setListener(new VpaListenerAdapter() { 373 | @Override public void onAnimationStart(View view) { 374 | dispatchChangeStarting(changeInfo.oldHolder, true); 375 | } 376 | 377 | @Override public void onAnimationEnd(View view) { 378 | oldViewAnim.setListener(null); 379 | ViewCompat.setAlpha(view, 1); 380 | ViewCompat.setTranslationX(view, 0); 381 | ViewCompat.setTranslationY(view, 0); 382 | dispatchChangeFinished(changeInfo.oldHolder, true); 383 | mChangeAnimations.remove(changeInfo.oldHolder); 384 | dispatchFinishedWhenDone(); 385 | } 386 | }).start(); 387 | } 388 | if (newView != null) { 389 | mChangeAnimations.add(changeInfo.newHolder); 390 | final ViewPropertyAnimatorCompat newViewAnimation = ViewCompat.animate(newView); 391 | newViewAnimation.translationX(0).translationY(0).setDuration(getChangeDuration()). 392 | alpha(1).setListener(new VpaListenerAdapter() { 393 | @Override public void onAnimationStart(View view) { 394 | dispatchChangeStarting(changeInfo.newHolder, false); 395 | } 396 | 397 | @Override public void onAnimationEnd(View view) { 398 | newViewAnimation.setListener(null); 399 | ViewCompat.setAlpha(newView, 1); 400 | ViewCompat.setTranslationX(newView, 0); 401 | ViewCompat.setTranslationY(newView, 0); 402 | dispatchChangeFinished(changeInfo.newHolder, false); 403 | mChangeAnimations.remove(changeInfo.newHolder); 404 | dispatchFinishedWhenDone(); 405 | } 406 | }).start(); 407 | } 408 | } 409 | 410 | private void endChangeAnimation(List infoList, ViewHolder item) { 411 | for (int i = infoList.size() - 1; i >= 0; i--) { 412 | ChangeInfo changeInfo = infoList.get(i); 413 | if (endChangeAnimationIfNecessary(changeInfo, item)) { 414 | if (changeInfo.oldHolder == null && changeInfo.newHolder == null) { 415 | infoList.remove(changeInfo); 416 | } 417 | } 418 | } 419 | } 420 | 421 | private void endChangeAnimationIfNecessary(ChangeInfo changeInfo) { 422 | if (changeInfo.oldHolder != null) { 423 | endChangeAnimationIfNecessary(changeInfo, changeInfo.oldHolder); 424 | } 425 | if (changeInfo.newHolder != null) { 426 | endChangeAnimationIfNecessary(changeInfo, changeInfo.newHolder); 427 | } 428 | } 429 | 430 | private boolean endChangeAnimationIfNecessary(ChangeInfo changeInfo, ViewHolder item) { 431 | boolean oldItem = false; 432 | if (changeInfo.newHolder == item) { 433 | changeInfo.newHolder = null; 434 | } else if (changeInfo.oldHolder == item) { 435 | changeInfo.oldHolder = null; 436 | oldItem = true; 437 | } else { 438 | return false; 439 | } 440 | ViewCompat.setAlpha(item.itemView, 1); 441 | ViewCompat.setTranslationX(item.itemView, 0); 442 | ViewCompat.setTranslationY(item.itemView, 0); 443 | dispatchChangeFinished(item, oldItem); 444 | return true; 445 | } 446 | 447 | @Override public void endAnimation(ViewHolder item) { 448 | final View view = item.itemView; 449 | // this will trigger end callback which should set properties to their target values. 450 | ViewCompat.animate(view).cancel(); 451 | // TODO if some other animations are chained to end, how do we cancel them as well? 452 | for (int i = mPendingMoves.size() - 1; i >= 0; i--) { 453 | MoveInfo moveInfo = mPendingMoves.get(i); 454 | if (moveInfo.holder == item) { 455 | ViewCompat.setTranslationY(view, 0); 456 | ViewCompat.setTranslationX(view, 0); 457 | dispatchMoveFinished(item); 458 | mPendingMoves.remove(i); 459 | } 460 | } 461 | endChangeAnimation(mPendingChanges, item); 462 | if (mPendingRemovals.remove(item)) { 463 | ViewHelper.clear(item.itemView); 464 | dispatchRemoveFinished(item); 465 | } 466 | if (mPendingAdditions.remove(item)) { 467 | ViewHelper.clear(item.itemView); 468 | dispatchAddFinished(item); 469 | } 470 | 471 | for (int i = mChangesList.size() - 1; i >= 0; i--) { 472 | ArrayList changes = mChangesList.get(i); 473 | endChangeAnimation(changes, item); 474 | if (changes.isEmpty()) { 475 | mChangesList.remove(i); 476 | } 477 | } 478 | for (int i = mMovesList.size() - 1; i >= 0; i--) { 479 | ArrayList moves = mMovesList.get(i); 480 | for (int j = moves.size() - 1; j >= 0; j--) { 481 | MoveInfo moveInfo = moves.get(j); 482 | if (moveInfo.holder == item) { 483 | ViewCompat.setTranslationY(view, 0); 484 | ViewCompat.setTranslationX(view, 0); 485 | dispatchMoveFinished(item); 486 | moves.remove(j); 487 | if (moves.isEmpty()) { 488 | mMovesList.remove(i); 489 | } 490 | break; 491 | } 492 | } 493 | } 494 | for (int i = mAdditionsList.size() - 1; i >= 0; i--) { 495 | ArrayList additions = mAdditionsList.get(i); 496 | if (additions.remove(item)) { 497 | ViewHelper.clear(item.itemView); 498 | dispatchAddFinished(item); 499 | if (additions.isEmpty()) { 500 | mAdditionsList.remove(i); 501 | } 502 | } 503 | } 504 | 505 | // animations should be ended by the cancel above. 506 | if (mRemoveAnimations.remove(item) && DEBUG) { 507 | throw new IllegalStateException( 508 | "after animation is cancelled, item should not be in " + "mRemoveAnimations list"); 509 | } 510 | 511 | if (mAddAnimations.remove(item) && DEBUG) { 512 | throw new IllegalStateException( 513 | "after animation is cancelled, item should not be in " + "mAddAnimations list"); 514 | } 515 | 516 | if (mChangeAnimations.remove(item) && DEBUG) { 517 | throw new IllegalStateException( 518 | "after animation is cancelled, item should not be in " + "mChangeAnimations list"); 519 | } 520 | 521 | if (mMoveAnimations.remove(item) && DEBUG) { 522 | throw new IllegalStateException( 523 | "after animation is cancelled, item should not be in " + "mMoveAnimations list"); 524 | } 525 | dispatchFinishedWhenDone(); 526 | } 527 | 528 | @Override public boolean isRunning() { 529 | return (!mPendingAdditions.isEmpty() || 530 | !mPendingChanges.isEmpty() || 531 | !mPendingMoves.isEmpty() || 532 | !mPendingRemovals.isEmpty() || 533 | !mMoveAnimations.isEmpty() || 534 | !mRemoveAnimations.isEmpty() || 535 | !mAddAnimations.isEmpty() || 536 | !mChangeAnimations.isEmpty() || 537 | !mMovesList.isEmpty() || 538 | !mAdditionsList.isEmpty() || 539 | !mChangesList.isEmpty()); 540 | } 541 | 542 | /** 543 | * Check the state of currently pending and running animations. If there are none 544 | * pending/running, call #dispatchAnimationsFinished() to notify any 545 | * listeners. 546 | */ 547 | private void dispatchFinishedWhenDone() { 548 | if (!isRunning()) { 549 | dispatchAnimationsFinished(); 550 | } 551 | } 552 | 553 | @Override public void endAnimations() { 554 | int count = mPendingMoves.size(); 555 | for (int i = count - 1; i >= 0; i--) { 556 | MoveInfo item = mPendingMoves.get(i); 557 | View view = item.holder.itemView; 558 | ViewCompat.setTranslationY(view, 0); 559 | ViewCompat.setTranslationX(view, 0); 560 | dispatchMoveFinished(item.holder); 561 | mPendingMoves.remove(i); 562 | } 563 | count = mPendingRemovals.size(); 564 | for (int i = count - 1; i >= 0; i--) { 565 | ViewHolder item = mPendingRemovals.get(i); 566 | dispatchRemoveFinished(item); 567 | mPendingRemovals.remove(i); 568 | } 569 | count = mPendingAdditions.size(); 570 | for (int i = count - 1; i >= 0; i--) { 571 | ViewHolder item = mPendingAdditions.get(i); 572 | ViewHelper.clear(item.itemView); 573 | dispatchAddFinished(item); 574 | mPendingAdditions.remove(i); 575 | } 576 | count = mPendingChanges.size(); 577 | for (int i = count - 1; i >= 0; i--) { 578 | endChangeAnimationIfNecessary(mPendingChanges.get(i)); 579 | } 580 | mPendingChanges.clear(); 581 | if (!isRunning()) { 582 | return; 583 | } 584 | 585 | int listCount = mMovesList.size(); 586 | for (int i = listCount - 1; i >= 0; i--) { 587 | ArrayList moves = mMovesList.get(i); 588 | count = moves.size(); 589 | for (int j = count - 1; j >= 0; j--) { 590 | MoveInfo moveInfo = moves.get(j); 591 | ViewHolder item = moveInfo.holder; 592 | View view = item.itemView; 593 | ViewCompat.setTranslationY(view, 0); 594 | ViewCompat.setTranslationX(view, 0); 595 | dispatchMoveFinished(moveInfo.holder); 596 | moves.remove(j); 597 | if (moves.isEmpty()) { 598 | mMovesList.remove(moves); 599 | } 600 | } 601 | } 602 | listCount = mAdditionsList.size(); 603 | for (int i = listCount - 1; i >= 0; i--) { 604 | ArrayList additions = mAdditionsList.get(i); 605 | count = additions.size(); 606 | for (int j = count - 1; j >= 0; j--) { 607 | ViewHolder item = additions.get(j); 608 | View view = item.itemView; 609 | ViewCompat.setAlpha(view, 1); 610 | dispatchAddFinished(item); 611 | //this check prevent exception when removal already happened during finishing animation 612 | if (j < additions.size()) { 613 | additions.remove(j); 614 | } 615 | if (additions.isEmpty()) { 616 | mAdditionsList.remove(additions); 617 | } 618 | } 619 | } 620 | listCount = mChangesList.size(); 621 | for (int i = listCount - 1; i >= 0; i--) { 622 | ArrayList changes = mChangesList.get(i); 623 | count = changes.size(); 624 | for (int j = count - 1; j >= 0; j--) { 625 | endChangeAnimationIfNecessary(changes.get(j)); 626 | if (changes.isEmpty()) { 627 | mChangesList.remove(changes); 628 | } 629 | } 630 | } 631 | 632 | cancelAll(mRemoveAnimations); 633 | cancelAll(mMoveAnimations); 634 | cancelAll(mAddAnimations); 635 | cancelAll(mChangeAnimations); 636 | 637 | dispatchAnimationsFinished(); 638 | } 639 | 640 | void cancelAll(List viewHolders) { 641 | for (int i = viewHolders.size() - 1; i >= 0; i--) { 642 | ViewCompat.animate(viewHolders.get(i).itemView).cancel(); 643 | } 644 | } 645 | 646 | private static class VpaListenerAdapter implements ViewPropertyAnimatorListener { 647 | 648 | @Override public void onAnimationStart(View view) { 649 | } 650 | 651 | @Override public void onAnimationEnd(View view) { 652 | } 653 | 654 | @Override public void onAnimationCancel(View view) { 655 | } 656 | } 657 | 658 | protected class DefaultAddVpaListener extends VpaListenerAdapter { 659 | 660 | ViewHolder mViewHolder; 661 | 662 | public DefaultAddVpaListener(final ViewHolder holder) { 663 | mViewHolder = holder; 664 | } 665 | 666 | @Override public void onAnimationStart(View view) { 667 | dispatchAddStarting(mViewHolder); 668 | } 669 | 670 | @Override public void onAnimationCancel(View view) { 671 | ViewHelper.clear(view); 672 | } 673 | 674 | @Override public void onAnimationEnd(View view) { 675 | ViewHelper.clear(view); 676 | dispatchAddFinished(mViewHolder); 677 | mAddAnimations.remove(mViewHolder); 678 | dispatchFinishedWhenDone(); 679 | } 680 | } 681 | 682 | protected class DefaultRemoveVpaListener extends VpaListenerAdapter { 683 | 684 | ViewHolder mViewHolder; 685 | 686 | public DefaultRemoveVpaListener(final ViewHolder holder) { 687 | mViewHolder = holder; 688 | } 689 | 690 | @Override public void onAnimationStart(View view) { 691 | dispatchRemoveStarting(mViewHolder); 692 | } 693 | 694 | @Override public void onAnimationCancel(View view) { 695 | ViewHelper.clear(view); 696 | } 697 | 698 | @Override public void onAnimationEnd(View view) { 699 | ViewHelper.clear(view); 700 | dispatchRemoveFinished(mViewHolder); 701 | mRemoveAnimations.remove(mViewHolder); 702 | dispatchFinishedWhenDone(); 703 | } 704 | } 705 | 706 | public static interface AnimateViewHolder { 707 | 708 | void preAnimateAddImpl(final ViewHolder holder); 709 | 710 | void preAnimateRemoveImpl(final ViewHolder holder); 711 | 712 | void animateAddImpl(final ViewHolder holder, ViewPropertyAnimatorListener listener); 713 | 714 | void animateRemoveImpl(final ViewHolder holder, 715 | ViewPropertyAnimatorListener listener); 716 | } 717 | } 718 | -------------------------------------------------------------------------------- /Timeline/app/src/main/java/com/lvr/timeline/anims/LandingAnimator.java: -------------------------------------------------------------------------------- 1 | package com.lvr.timeline.anims; 2 | 3 | /** 4 | * Copyright (C) 2017 Wasabeef 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | import android.support.v4.view.ViewCompat; 20 | import android.support.v7.widget.RecyclerView; 21 | import android.view.animation.Interpolator; 22 | 23 | public class LandingAnimator extends BaseItemAnimator { 24 | 25 | public LandingAnimator() { 26 | } 27 | 28 | public LandingAnimator(Interpolator interpolator) { 29 | mInterpolator = interpolator; 30 | } 31 | 32 | @Override protected void animateRemoveImpl(final RecyclerView.ViewHolder holder) { 33 | ViewCompat.animate(holder.itemView) 34 | .alpha(0) 35 | .scaleX(1.5f) 36 | .scaleY(1.5f) 37 | .setDuration(getRemoveDuration()) 38 | .setInterpolator(mInterpolator) 39 | .setListener(new DefaultRemoveVpaListener(holder)) 40 | .setStartDelay(getRemoveDelay(holder)) 41 | .start(); 42 | } 43 | 44 | @Override protected void preAnimateAddImpl(RecyclerView.ViewHolder holder) { 45 | ViewCompat.setAlpha(holder.itemView, 0); 46 | ViewCompat.setScaleX(holder.itemView, 1.5f); 47 | ViewCompat.setScaleY(holder.itemView, 1.5f); 48 | } 49 | 50 | @Override protected void animateAddImpl(final RecyclerView.ViewHolder holder) { 51 | ViewCompat.animate(holder.itemView) 52 | .alpha(1) 53 | .scaleX(1) 54 | .scaleY(1) 55 | .setDuration(getAddDuration()) 56 | .setInterpolator(mInterpolator) 57 | .setListener(new DefaultAddVpaListener(holder)) 58 | .setStartDelay(getAddDelay(holder)) 59 | .start(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Timeline/app/src/main/java/com/lvr/timeline/anims/ScaleInAnimationAdapter.java: -------------------------------------------------------------------------------- 1 | package com.lvr.timeline.anims; 2 | 3 | import android.animation.Animator; 4 | import android.animation.ObjectAnimator; 5 | import android.support.v7.widget.RecyclerView; 6 | import android.view.View; 7 | 8 | /** 9 | * Copyright (C) 2017 Wasabeef 10 | * 11 | * Licensed under the Apache License, Version 2.0 (the "License"); 12 | * you may not use this file except in compliance with the License. 13 | * You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, software 18 | * distributed under the License is distributed on an "AS IS" BASIS, 19 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | * See the License for the specific language governing permissions and 21 | * limitations under the License. 22 | */ 23 | 24 | public class ScaleInAnimationAdapter extends AnimationAdapter { 25 | 26 | private static final float DEFAULT_SCALE_FROM = .5f; 27 | private final float mFrom; 28 | 29 | public ScaleInAnimationAdapter(RecyclerView.Adapter adapter) { 30 | this(adapter, DEFAULT_SCALE_FROM); 31 | } 32 | 33 | public ScaleInAnimationAdapter(RecyclerView.Adapter adapter, float from) { 34 | super(adapter); 35 | mFrom = from; 36 | } 37 | 38 | @Override protected Animator[] getAnimators(View view) { 39 | ObjectAnimator scaleX = ObjectAnimator.ofFloat(view, "scaleX", mFrom, 1f); 40 | ObjectAnimator scaleY = ObjectAnimator.ofFloat(view, "scaleY", mFrom, 1f); 41 | return new ObjectAnimator[] { scaleX, scaleY }; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Timeline/app/src/main/java/com/lvr/timeline/anims/ViewHelper.java: -------------------------------------------------------------------------------- 1 | package com.lvr.timeline.anims; 2 | 3 | import android.support.v4.view.ViewCompat; 4 | import android.view.View; 5 | 6 | /** 7 | * Copyright (C) 2017 Wasabeef 8 | * 9 | * Licensed under the Apache License, Version 2.0 (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * http://www.apache.org/licenses/LICENSE-2.0 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | */ 21 | 22 | public final class ViewHelper { 23 | 24 | public static void clear(View v) { 25 | ViewCompat.setAlpha(v, 1); 26 | ViewCompat.setScaleY(v, 1); 27 | ViewCompat.setScaleX(v, 1); 28 | ViewCompat.setTranslationY(v, 0); 29 | ViewCompat.setTranslationX(v, 0); 30 | ViewCompat.setRotation(v, 0); 31 | ViewCompat.setRotationY(v, 0); 32 | ViewCompat.setRotationX(v, 0); 33 | ViewCompat.setPivotY(v, v.getMeasuredHeight() / 2); 34 | ViewCompat.setPivotX(v, v.getMeasuredWidth() / 2); 35 | ViewCompat.animate(v).setInterpolator(null).setStartDelay(0); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Timeline/app/src/main/java/com/lvr/timeline/app/AppApplication.java: -------------------------------------------------------------------------------- 1 | package com.lvr.timeline.app; 2 | 3 | 4 | import com.lvr.timeline.base.BaseApplication; 5 | 6 | import io.realm.Realm; 7 | import io.realm.RealmConfiguration; 8 | 9 | /** 10 | * APPLICATION 11 | */ 12 | public class AppApplication extends BaseApplication { 13 | public static Realm REALM_INSTANCE; 14 | @Override 15 | public void onCreate() { 16 | super.onCreate(); 17 | Realm.init(this); 18 | RealmConfiguration config = new RealmConfiguration.Builder() 19 | .name("TimeLineRealm.realm") 20 | .deleteRealmIfMigrationNeeded() 21 | .build(); 22 | REALM_INSTANCE = Realm.getInstance(config); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Timeline/app/src/main/java/com/lvr/timeline/app/AppConstantValue.java: -------------------------------------------------------------------------------- 1 | package com.lvr.timeline.app; 2 | 3 | /** 4 | * Created by lvr on 2017/4/17. 5 | */ 6 | 7 | public class AppConstantValue { 8 | public static final String HINT_TITLE ="标题不能为空"; 9 | public static final String HINT_TIME ="时间段设置错误"; 10 | public static final String HINT_MISS ="少壮不努力,老大徒伤悲!\n时间已经过去了......"; 11 | //滑动删除按钮 关闭 12 | public static final int DELETE_STATE_CLOSE = 0; 13 | //滑动删除按钮 开着 14 | public static final int DELETE_STATE_OPEN = 1; 15 | //滑动删除按钮 正在关闭 16 | public static final int DELETE_STATE_CLOSEING = 2; 17 | //滑动删除按钮 正在开 18 | public static final int DELETE_STATE_OPENING =3; 19 | 20 | } 21 | -------------------------------------------------------------------------------- /Timeline/app/src/main/java/com/lvr/timeline/app/AppManager.java: -------------------------------------------------------------------------------- 1 | 2 | package com.lvr.timeline.app; 3 | 4 | import android.app.Activity; 5 | import android.app.ActivityManager; 6 | import android.content.Context; 7 | 8 | import java.util.Stack; 9 | 10 | /** 11 | * activity管理 12 | */ 13 | public class AppManager { 14 | private static Stack activityStack; 15 | private volatile static AppManager instance; 16 | 17 | private AppManager() { 18 | 19 | } 20 | /** 21 | * 单一实例 22 | */ 23 | public static AppManager getAppManager() { 24 | if (instance == null) { 25 | synchronized (AppManager.class){ 26 | if(instance==null){ 27 | instance = new AppManager(); 28 | instance.activityStack = new Stack(); 29 | } 30 | } 31 | 32 | } 33 | return instance; 34 | } 35 | 36 | /** 37 | * 添加Activity到堆栈 38 | */ 39 | public void addActivity(Activity activity) { 40 | if (activityStack == null) { 41 | activityStack = new Stack(); 42 | } 43 | activityStack.add(activity); 44 | } 45 | 46 | /** 47 | * 获取当前Activity(堆栈中最后一个压入的) 48 | */ 49 | public Activity currentActivity() { 50 | try { 51 | Activity activity = activityStack.lastElement(); 52 | return activity; 53 | } catch (Exception e) { 54 | // e.printStackTrace(); 55 | return null; 56 | } 57 | } 58 | 59 | /** 60 | * 获取当前Activity的前一个Activity 61 | */ 62 | public Activity preActivity() { 63 | int index = activityStack.size() - 2; 64 | if (index < 0) { 65 | return null; 66 | } 67 | Activity activity = activityStack.get(index); 68 | return activity; 69 | } 70 | 71 | /** 72 | * 结束当前Activity(堆栈中最后一个压入的) 73 | */ 74 | public void finishActivity() { 75 | Activity activity = activityStack.lastElement(); 76 | finishActivity(activity); 77 | } 78 | 79 | /** 80 | * 结束指定的Activity 81 | */ 82 | public void finishActivity(Activity activity) { 83 | if (activity != null) { 84 | activityStack.remove(activity); 85 | activity.finish(); 86 | activity = null; 87 | } 88 | } 89 | 90 | /** 91 | * 移除指定的Activity 92 | */ 93 | public void removeActivity(Activity activity) { 94 | if (activity != null) { 95 | activityStack.remove(activity); 96 | activity = null; 97 | } 98 | } 99 | 100 | /** 101 | * 结束指定类名的Activity 102 | */ 103 | public void finishActivity(Class cls) { 104 | try { 105 | for (Activity activity : activityStack) { 106 | if (activity.getClass().equals(cls)) { 107 | finishActivity(activity); 108 | } 109 | } 110 | } catch (Exception e) { 111 | e.printStackTrace(); 112 | } 113 | 114 | } 115 | 116 | /** 117 | * 结束所有Activity 118 | */ 119 | public void finishAllActivity() { 120 | for (int i = 0, size = activityStack.size(); i < size; i++) { 121 | if (null != activityStack.get(i)) { 122 | activityStack.get(i).finish(); 123 | } 124 | } 125 | activityStack.clear(); 126 | } 127 | 128 | /** 129 | * 返回到指定的activity 130 | * 131 | * @param cls 132 | */ 133 | public void returnToActivity(Class cls) { 134 | while (activityStack.size() != 0) 135 | if (activityStack.peek().getClass() == cls) { 136 | break; 137 | } else { 138 | finishActivity(activityStack.peek()); 139 | } 140 | } 141 | 142 | 143 | /** 144 | * 是否已经打开指定的activity 145 | * @param cls 146 | * @return 147 | */ 148 | public boolean isOpenActivity(Class cls) { 149 | if (activityStack!=null){ 150 | for (int i = 0, size = activityStack.size(); i < size; i++) { 151 | if (cls == activityStack.peek().getClass()) { 152 | return true; 153 | } 154 | } 155 | } 156 | return false; 157 | } 158 | 159 | /** 160 | * 退出应用程序 161 | * 162 | * @param context 上下文 163 | * @param isBackground 是否开开启后台运行 164 | */ 165 | public void AppExit(Context context, Boolean isBackground) { 166 | try { 167 | finishAllActivity(); 168 | ActivityManager activityMgr = (ActivityManager) context 169 | .getSystemService(Context.ACTIVITY_SERVICE); 170 | activityMgr.restartPackage(context.getPackageName()); 171 | } catch (Exception e) { 172 | 173 | } finally { 174 | // 注意,如果您有后台程序运行,请不要支持此句子 175 | if (!isBackground) { 176 | System.exit(0); 177 | } 178 | } 179 | } 180 | } -------------------------------------------------------------------------------- /Timeline/app/src/main/java/com/lvr/timeline/base/BaseActivity.java: -------------------------------------------------------------------------------- 1 | package com.lvr.timeline.base; 2 | 3 | 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.content.pm.ActivityInfo; 7 | import android.os.Bundle; 8 | import android.support.v7.app.AppCompatActivity; 9 | import android.view.Window; 10 | 11 | import com.lvr.timeline.R; 12 | import com.lvr.timeline.app.AppManager; 13 | import com.lvr.timeline.utils.StatusBarSetting; 14 | 15 | import butterknife.ButterKnife; 16 | import butterknife.Unbinder; 17 | 18 | /** 19 | * 基类 20 | */ 21 | 22 | public abstract class BaseActivity extends AppCompatActivity { 23 | 24 | public Context mContext; 25 | private Unbinder mUnbinder; 26 | private int count;//记录开启进度条的情况 只能开一个 27 | 28 | @Override 29 | public void onCreate(Bundle savedInstanceState) { 30 | super.onCreate(savedInstanceState); 31 | doBeforeSetcontentView(); 32 | setContentView(getLayoutId()); 33 | // 默认着色状态栏 34 | SetStatusBarColor(); 35 | mUnbinder = ButterKnife.bind(this); 36 | mContext = this; 37 | 38 | this.initPresenter(); 39 | this.initView(); 40 | } 41 | 42 | /** 43 | * 设置layout前配置 44 | */ 45 | private void doBeforeSetcontentView() { 46 | // 把actvity放到application栈中管理 47 | AppManager.getAppManager().addActivity(this); 48 | // 无标题 49 | requestWindowFeature(Window.FEATURE_NO_TITLE); 50 | // 设置竖屏 51 | setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); 52 | 53 | 54 | } 55 | 56 | /********************* 57 | * 子类实现 58 | *****************************/ 59 | //获取布局文件 60 | public abstract int getLayoutId(); 61 | 62 | //简单页面无需mvp就不用管此方法即可,完美兼容各种实际场景的变通 63 | public abstract void initPresenter(); 64 | 65 | //初始化view 66 | public abstract void initView(); 67 | 68 | /** 69 | * 着色状态栏(4.4以上系统有效) 70 | */ 71 | protected void SetStatusBarColor() { 72 | StatusBarSetting.setColor(this, getResources().getColor(R.color.colorPrimary)); 73 | } 74 | 75 | /** 76 | * 着色状态栏(4.4以上系统有效) 77 | */ 78 | protected void SetStatusBarColor(int color) { 79 | StatusBarSetting.setColor(this, color); 80 | } 81 | 82 | /** 83 | * 沉浸状态栏(4.4以上系统有效) 84 | */ 85 | protected void SetTranslanteBar() { 86 | StatusBarSetting.setTranslucent(this); 87 | } 88 | 89 | 90 | /** 91 | * 通过Class跳转界面 92 | **/ 93 | public void startActivity(Class cls) { 94 | startActivity(cls, null); 95 | } 96 | 97 | /** 98 | * 通过Class跳转界面 99 | **/ 100 | public void startActivityForResult(Class cls, int requestCode) { 101 | startActivityForResult(cls, null, requestCode); 102 | } 103 | 104 | /** 105 | * 含有Bundle通过Class跳转界面 106 | **/ 107 | public void startActivityForResult(Class cls, Bundle bundle, 108 | int requestCode) { 109 | Intent intent = new Intent(); 110 | intent.setClass(this, cls); 111 | if (bundle != null) { 112 | intent.putExtras(bundle); 113 | } 114 | startActivityForResult(intent, requestCode); 115 | } 116 | 117 | /** 118 | * 含有Bundle通过Class跳转界面 119 | **/ 120 | public void startActivity(Class cls, Bundle bundle) { 121 | Intent intent = new Intent(); 122 | intent.setClass(this, cls); 123 | if (bundle != null) { 124 | intent.putExtras(bundle); 125 | } 126 | startActivity(intent); 127 | } 128 | 129 | 130 | 131 | @Override 132 | protected void onResume() { 133 | super.onResume(); 134 | 135 | } 136 | 137 | @Override 138 | protected void onPause() { 139 | super.onPause(); 140 | 141 | } 142 | 143 | 144 | @Override 145 | protected void onDestroy() { 146 | super.onDestroy(); 147 | mUnbinder.unbind(); 148 | AppManager.getAppManager().finishActivity(this); 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /Timeline/app/src/main/java/com/lvr/timeline/base/BaseApplication.java: -------------------------------------------------------------------------------- 1 | package com.lvr.timeline.base; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | import android.content.res.Resources; 6 | 7 | /** 8 | * APPLICATION 9 | */ 10 | public class BaseApplication extends Application { 11 | 12 | private static BaseApplication baseApplication; 13 | 14 | @Override 15 | public void onCreate() { 16 | super.onCreate(); 17 | baseApplication = this; 18 | } 19 | 20 | public static Context getAppContext() { 21 | return baseApplication; 22 | } 23 | public static Resources getAppResources() { 24 | return baseApplication.getResources(); 25 | } 26 | @Override 27 | public void onTerminate() { 28 | super.onTerminate(); 29 | } 30 | 31 | 32 | 33 | } 34 | -------------------------------------------------------------------------------- /Timeline/app/src/main/java/com/lvr/timeline/base/BaseFragment.java: -------------------------------------------------------------------------------- 1 | package com.lvr.timeline.base; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.annotation.Nullable; 6 | import android.support.v4.app.Fragment; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | 11 | 12 | import butterknife.ButterKnife; 13 | import butterknife.Unbinder; 14 | 15 | 16 | public abstract class BaseFragment extends Fragment { 17 | protected View rootView; 18 | 19 | private Unbinder mUnbinder; 20 | private int count;//记录开启进度条的情况 只能开一个 21 | //当前Fragment是否处于可见状态标志,防止因ViewPager的缓存机制而导致回调函数的触发 22 | private boolean isFragmentVisible; 23 | //是否是第一次开启网络加载 24 | public boolean isFirst; 25 | 26 | @Nullable 27 | @Override 28 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 29 | if (rootView == null) 30 | rootView = inflater.inflate(getLayoutResource(), container, false); 31 | mUnbinder = ButterKnife.bind(this, rootView); 32 | initView(); 33 | //可见,但是并没有加载过 34 | if (isFragmentVisible && !isFirst) { 35 | onFragmentVisibleChange(true); 36 | } 37 | return rootView; 38 | } 39 | 40 | //获取布局文件 41 | protected abstract int getLayoutResource(); 42 | 43 | 44 | //初始化view 45 | protected abstract void initView(); 46 | 47 | 48 | /** 49 | * 通过Class跳转界面 50 | **/ 51 | public void startActivity(Class cls) { 52 | startActivity(cls, null); 53 | } 54 | 55 | /** 56 | * 通过Class跳转界面 57 | **/ 58 | public void startActivityForResult(Class cls, int requestCode) { 59 | startActivityForResult(cls, null, requestCode); 60 | } 61 | 62 | /** 63 | * 含有Bundle通过Class跳转界面 64 | **/ 65 | public void startActivityForResult(Class cls, Bundle bundle, 66 | int requestCode) { 67 | Intent intent = new Intent(); 68 | intent.setClass(getActivity(), cls); 69 | if (bundle != null) { 70 | intent.putExtras(bundle); 71 | } 72 | startActivityForResult(intent, requestCode); 73 | } 74 | 75 | /** 76 | * 含有Bundle通过Class跳转界面 77 | **/ 78 | public void startActivity(Class cls, Bundle bundle) { 79 | Intent intent = new Intent(); 80 | intent.setClass(getActivity(), cls); 81 | if (bundle != null) { 82 | intent.putExtras(bundle); 83 | } 84 | startActivity(intent); 85 | } 86 | 87 | @Override 88 | public void setUserVisibleHint(boolean isVisibleToUser) { 89 | super.setUserVisibleHint(isVisibleToUser); 90 | if (isVisibleToUser) { 91 | isFragmentVisible = true; 92 | } 93 | if (rootView == null) { 94 | return; 95 | } 96 | //可见,并且没有加载过 97 | if (!isFirst&&isFragmentVisible) { 98 | onFragmentVisibleChange(true); 99 | return; 100 | } 101 | //由可见——>不可见 已经加载过 102 | if (isFragmentVisible) { 103 | onFragmentVisibleChange(false); 104 | isFragmentVisible = false; 105 | } 106 | } 107 | 108 | 109 | 110 | @Override 111 | public void onDestroyView() { 112 | super.onDestroyView(); 113 | mUnbinder.unbind(); 114 | } 115 | 116 | /** 117 | * 当前fragment可见状态发生变化时会回调该方法 118 | * 如果当前fragment是第一次加载,等待onCreateView后才会回调该方法,其它情况回调时机跟 {@link #setUserVisibleHint(boolean)}一致 119 | * 在该回调方法中你可以做一些加载数据操作,甚至是控件的操作. 120 | * 121 | * @param isVisible true 不可见 -> 可见 122 | * false 可见 -> 不可见 123 | */ 124 | protected void onFragmentVisibleChange(boolean isVisible) { 125 | 126 | } 127 | 128 | 129 | } 130 | -------------------------------------------------------------------------------- /Timeline/app/src/main/java/com/lvr/timeline/base/BaseFragmentAdapter.java: -------------------------------------------------------------------------------- 1 | package com.lvr.timeline.base; 2 | 3 | import android.support.v4.app.Fragment; 4 | import android.support.v4.app.FragmentManager; 5 | import android.support.v4.app.FragmentPagerAdapter; 6 | 7 | import com.lvr.timeline.utils.CollectionUtils; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | public class BaseFragmentAdapter extends FragmentPagerAdapter { 13 | 14 | List fragmentList = new ArrayList(); 15 | private List mTitles; 16 | 17 | public BaseFragmentAdapter(FragmentManager fm, List fragmentList) { 18 | super(fm); 19 | this.fragmentList = fragmentList; 20 | } 21 | 22 | public BaseFragmentAdapter(FragmentManager fm, List fragmentList, List mTitles) { 23 | super(fm); 24 | this.fragmentList = fragmentList; 25 | this.mTitles = mTitles; 26 | } 27 | 28 | @Override 29 | public CharSequence getPageTitle(int position) { 30 | return !CollectionUtils.isNullOrEmpty(mTitles) ? mTitles.get(position) : ""; 31 | } 32 | 33 | @Override 34 | public Fragment getItem(int position) { 35 | return fragmentList.get(position); 36 | } 37 | 38 | @Override 39 | public int getCount() { 40 | return fragmentList.size(); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /Timeline/app/src/main/java/com/lvr/timeline/bean/TimeInfo.java: -------------------------------------------------------------------------------- 1 | package com.lvr.timeline.bean; 2 | 3 | 4 | import android.os.Parcel; 5 | import android.os.Parcelable; 6 | 7 | import io.realm.RealmObject; 8 | import io.realm.annotations.PrimaryKey; 9 | 10 | public class TimeInfo extends RealmObject implements Parcelable { 11 | //年月日信息 12 | private String ymD; 13 | //标题 14 | private String title; 15 | //内容 16 | private String content; 17 | //时间段信息 12:00-13:00 18 | @PrimaryKey 19 | private String addTime; 20 | 21 | public String getCreateTime() { 22 | return createTime; 23 | } 24 | 25 | public void setCreateTime(String createTime) { 26 | this.createTime = createTime; 27 | } 28 | 29 | //创建该条目时的时间 30 | private String createTime; 31 | //是否是新的任务 32 | private boolean isNew = true; 33 | //在列表中所处位置 34 | private int position = -1; 35 | 36 | public int getPosition() { 37 | return position; 38 | } 39 | 40 | public void setPosition(int position) { 41 | this.position = position; 42 | } 43 | 44 | 45 | 46 | public boolean isNew() { 47 | return isNew; 48 | } 49 | 50 | public void setNew(boolean aNew) { 51 | isNew = aNew; 52 | } 53 | 54 | 55 | 56 | public String getTitle() { 57 | return title; 58 | } 59 | 60 | public void setTitle(String title) { 61 | this.title = title; 62 | } 63 | 64 | public String getContent() { 65 | return content; 66 | } 67 | 68 | public void setContent(String content) { 69 | this.content = content; 70 | } 71 | 72 | public String getYmD() { 73 | return ymD; 74 | } 75 | 76 | public void setYmD(String ymD) { 77 | this.ymD = ymD; 78 | } 79 | 80 | public String getAddTime() { 81 | return addTime; 82 | } 83 | 84 | public void setAddTime(String addTime) { 85 | this.addTime = addTime; 86 | } 87 | 88 | @Override 89 | public String toString() { 90 | return "TimeInfo{" + 91 | "ymD='" + ymD + '\'' + 92 | ", title='" + title + '\'' + 93 | ", content='" + content + '\'' + 94 | ", addTime='" + addTime + '\'' + 95 | '}'; 96 | } 97 | 98 | @Override 99 | public int describeContents() { 100 | return 0; 101 | } 102 | 103 | @Override 104 | public void writeToParcel(Parcel dest, int flags) { 105 | dest.writeString(this.ymD); 106 | dest.writeString(this.title); 107 | dest.writeString(this.content); 108 | dest.writeString(this.addTime); 109 | } 110 | 111 | public TimeInfo() { 112 | } 113 | 114 | protected TimeInfo(Parcel in) { 115 | this.ymD = in.readString(); 116 | this.title = in.readString(); 117 | this.content = in.readString(); 118 | this.addTime = in.readString(); 119 | } 120 | 121 | public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { 122 | @Override 123 | public TimeInfo createFromParcel(Parcel source) { 124 | return new TimeInfo(source); 125 | } 126 | 127 | @Override 128 | public TimeInfo[] newArray(int size) { 129 | return new TimeInfo[size]; 130 | } 131 | }; 132 | } 133 | -------------------------------------------------------------------------------- /Timeline/app/src/main/java/com/lvr/timeline/home/fragment/CountDownFragment.java: -------------------------------------------------------------------------------- 1 | package com.lvr.timeline.home.fragment; 2 | 3 | import com.lvr.timeline.R; 4 | import com.lvr.timeline.base.BaseFragment; 5 | 6 | /** 7 | * Created by lvr on 2017/4/8. 8 | */ 9 | 10 | public class CountDownFragment extends BaseFragment { 11 | @Override 12 | protected int getLayoutResource() { 13 | return R.layout.fragment_countdown; 14 | } 15 | 16 | @Override 17 | protected void initView() { 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Timeline/app/src/main/java/com/lvr/timeline/home/fragment/TodayFragment.java: -------------------------------------------------------------------------------- 1 | package com.lvr.timeline.home.fragment; 2 | 3 | import android.content.Intent; 4 | import android.support.v7.widget.LinearLayoutManager; 5 | import android.support.v7.widget.RecyclerView; 6 | import android.widget.Toast; 7 | 8 | import com.lvr.timeline.R; 9 | import com.lvr.timeline.adapter.TimelineAdapter; 10 | import com.lvr.timeline.anims.LandingAnimator; 11 | import com.lvr.timeline.anims.ScaleInAnimationAdapter; 12 | import com.lvr.timeline.app.AppApplication; 13 | import com.lvr.timeline.base.BaseFragment; 14 | import com.lvr.timeline.bean.TimeInfo; 15 | import com.lvr.timeline.home.ui.EditActivity; 16 | import com.lvr.timeline.utils.RealmOperationHelper; 17 | 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | 21 | import butterknife.BindView; 22 | import de.greenrobot.event.EventBus; 23 | import de.greenrobot.event.Subscribe; 24 | import io.realm.RealmChangeListener; 25 | import io.realm.RealmResults; 26 | 27 | /** 28 | * Created by lvr on 2017/4/8. 29 | */ 30 | 31 | public class TodayFragment extends BaseFragment implements TimelineAdapter.OnItemClickListener{ 32 | @BindView(R.id.rv_record) 33 | RecyclerView mRvRecord; 34 | private TimelineAdapter mAdapter; 35 | private List mInfos = new ArrayList<>(); 36 | @Override 37 | protected int getLayoutResource() { 38 | return R.layout.fragment_today; 39 | } 40 | 41 | @Override 42 | protected void initView() { 43 | mAdapter = new TimelineAdapter(getActivity(),mInfos); 44 | mRvRecord.setLayoutManager(new LinearLayoutManager(getActivity(),LinearLayoutManager.VERTICAL,false)); 45 | mAdapter.setOnItemClickListener(this); 46 | mRvRecord.setItemAnimator(new LandingAnimator()); 47 | ScaleInAnimationAdapter scaleAdapter = new ScaleInAnimationAdapter(mAdapter); 48 | scaleAdapter.setFirstOnly(false); 49 | scaleAdapter.setDuration(500); 50 | mRvRecord.setAdapter(scaleAdapter); 51 | mRvRecord.getItemAnimator().setAddDuration(300); 52 | mRvRecord.getItemAnimator().setChangeDuration(300); 53 | mRvRecord.getItemAnimator().setMoveDuration(300); 54 | mRvRecord.getItemAnimator().setRemoveDuration(300); 55 | if(!EventBus.getDefault().isRegistered(this)){ 56 | EventBus.getDefault().register(this); 57 | } 58 | coverData(); 59 | } 60 | 61 | /** 62 | * 恢复数据库中数据 63 | */ 64 | private void coverData() { 65 | final RealmResults results = (RealmResults) RealmOperationHelper.getInstance(AppApplication.REALM_INSTANCE).queryAllAsync(TimeInfo.class); 66 | // TODO: 2017/4/20 开启加载提示 67 | results.addChangeListener(new RealmChangeListener>() { 68 | @Override 69 | public void onChange(RealmResults element) { 70 | results.removeAllChangeListeners(); 71 | // TODO: 2017/4/20 关闭加载提示 72 | Toast.makeText(getActivity(), "加载数据完毕", Toast.LENGTH_SHORT).show(); 73 | System.out.println("从数据库中查找到的条目:" + element.size()); 74 | if (element == null) { 75 | return; 76 | } 77 | mInfos.addAll(element); 78 | System.out.println("复制后的数据条目:" + mInfos.size()); 79 | mAdapter.notifyDataSetChanged(); 80 | } 81 | }); 82 | 83 | 84 | 85 | } 86 | 87 | @Subscribe 88 | public void onTimeInfoEvent(final TimeInfo info) { 89 | if(info.isNew()){ 90 | mInfos.add(info); 91 | mAdapter.notifyItemRangeInserted(mInfos.size(),1); 92 | RealmOperationHelper.getInstance(AppApplication.REALM_INSTANCE).add(info); 93 | System.out.println("添加一个新数据"); 94 | }else{ 95 | if(info.getPosition()!=-1){ 96 | mInfos.remove(info.getPosition()); 97 | mInfos.add(info.getPosition(),info); 98 | mAdapter.notifyItemChanged(info.getPosition()); 99 | } 100 | } 101 | 102 | } 103 | 104 | 105 | @Override 106 | public void onItemClick(TimeInfo mInfo,int position) { 107 | Intent intent = new Intent(getActivity(), EditActivity.class); 108 | intent.putExtra("TimeInfo",mInfo); 109 | intent.putExtra("position",position); 110 | startActivity(intent); 111 | } 112 | 113 | @Override 114 | public void onDestroy() { 115 | super.onDestroy(); 116 | AppApplication.REALM_INSTANCE.close(); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /Timeline/app/src/main/java/com/lvr/timeline/home/fragment/YesterdayFragment.java: -------------------------------------------------------------------------------- 1 | package com.lvr.timeline.home.fragment; 2 | 3 | import com.lvr.timeline.R; 4 | import com.lvr.timeline.base.BaseFragment; 5 | 6 | /** 7 | * Created by lvr on 2017/4/8. 8 | */ 9 | 10 | public class YesterdayFragment extends BaseFragment { 11 | @Override 12 | protected int getLayoutResource() { 13 | return R.layout.fragment_yesterday; 14 | } 15 | 16 | @Override 17 | protected void initView() { 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Timeline/app/src/main/java/com/lvr/timeline/home/ui/EditActivity.java: -------------------------------------------------------------------------------- 1 | package com.lvr.timeline.home.ui; 2 | 3 | 4 | import android.content.DialogInterface; 5 | import android.content.Intent; 6 | import android.os.Bundle; 7 | import android.support.v7.app.AlertDialog; 8 | import android.support.v7.widget.Toolbar; 9 | import android.text.TextUtils; 10 | import android.util.Log; 11 | import android.view.LayoutInflater; 12 | import android.view.Menu; 13 | import android.view.MenuItem; 14 | import android.view.View; 15 | import android.widget.Button; 16 | import android.widget.EditText; 17 | import android.widget.LinearLayout; 18 | import android.widget.TextView; 19 | 20 | import com.borax12.materialdaterangepicker.time.RadialPickerLayout; 21 | import com.borax12.materialdaterangepicker.time.TimePickerDialog; 22 | import com.lvr.timeline.R; 23 | import com.lvr.timeline.app.AppConstantValue; 24 | import com.lvr.timeline.base.BaseActivity; 25 | import com.lvr.timeline.bean.TimeInfo; 26 | 27 | import java.text.SimpleDateFormat; 28 | import java.util.Calendar; 29 | import java.util.Date; 30 | import java.util.GregorianCalendar; 31 | 32 | import butterknife.BindView; 33 | import de.greenrobot.event.EventBus; 34 | 35 | /** 36 | * Created by lvr on 2017/4/8. 37 | */ 38 | 39 | public class EditActivity extends BaseActivity implements View.OnClickListener, TimePickerDialog.OnTimeSetListener { 40 | @BindView(R.id.toolbar) 41 | Toolbar mToolbar; 42 | @BindView(R.id.ed_title) 43 | EditText mEdTitle; 44 | @BindView(R.id.ed_content) 45 | EditText mEdContent; 46 | @BindView(R.id.ll_root) 47 | LinearLayout mLlRoot; 48 | @BindView(R.id.tv_time) 49 | TextView mTvTime; 50 | @BindView(R.id.tv_today) 51 | TextView mTvToday; 52 | private TimeInfo mInfo; 53 | private int mPosition = -2; 54 | private AlertDialog mDialog; 55 | 56 | @Override 57 | public int getLayoutId() { 58 | return R.layout.activity_edit; 59 | } 60 | 61 | @Override 62 | public void initPresenter() { 63 | 64 | } 65 | 66 | @Override 67 | public void initView() { 68 | mToolbar.setTitle("添加任务"); 69 | mToolbar.setTitleTextColor(getResources().getColor(R.color.white)); 70 | setSupportActionBar(mToolbar); 71 | mToolbar.setNavigationOnClickListener(new View.OnClickListener() { 72 | @Override 73 | public void onClick(View view) { 74 | onBackPressed(); 75 | } 76 | }); 77 | Intent intent = getIntent(); 78 | setPreInformation(intent); 79 | mTvTime.setOnClickListener(this); 80 | } 81 | 82 | /** 83 | * 初始化时间 84 | */ 85 | private void initDate() { 86 | SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm"); 87 | String allTime = format.format(new Date()); 88 | int index = allTime.indexOf(" "); 89 | String ymd = allTime.substring(0, index); 90 | String hm = allTime.substring(index + 1); 91 | setDate(ymd, hm); 92 | } 93 | 94 | /** 95 | * 设置启动Intent时,携带的信息 96 | * 97 | * @param intent 98 | */ 99 | private void setPreInformation(Intent intent) { 100 | if (intent != null) { 101 | Bundle extras = intent.getExtras(); 102 | if (extras != null) { 103 | mInfo = (TimeInfo) extras.get("TimeInfo"); 104 | if (mInfo != null) { 105 | if (mInfo.getTitle() != null) { 106 | mEdTitle.setText(mInfo.getTitle()); 107 | } 108 | if (mInfo.getContent() != null) { 109 | mEdContent.setText(mInfo.getContent()); 110 | } 111 | if (mInfo.getYmD() != null) { 112 | mTvToday.setText(mInfo.getYmD()); 113 | } 114 | if (mInfo.getAddTime() != null) { 115 | mTvTime.setText(mInfo.getAddTime()); 116 | } 117 | mInfo.setNew(false); 118 | } 119 | mPosition = (int) extras.get("position"); 120 | if (mPosition != -2) { 121 | mInfo.setPosition(mPosition); 122 | } 123 | 124 | } else { 125 | initDate(); 126 | } 127 | } 128 | 129 | } 130 | 131 | 132 | @Override 133 | public boolean onCreateOptionsMenu(Menu menu) { 134 | getMenuInflater().inflate(R.menu.menu_toolabr, menu); 135 | MenuItem item = menu.findItem(R.id.mn_it_edit); 136 | item.setIcon(R.drawable.mn_right); 137 | item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { 138 | @Override 139 | public boolean onMenuItemClick(MenuItem item) { 140 | if (verfyContent()) { 141 | String title = mEdTitle.getText().toString(); 142 | System.out.println("title的内容:" + title); 143 | String content = mEdContent.getText().toString(); 144 | if (mInfo == null) { 145 | mInfo = new TimeInfo(); 146 | } 147 | mInfo.setTitle(title); 148 | mInfo.setContent(content); 149 | mInfo.setYmD(mTvToday.getText().toString()); 150 | mInfo.setAddTime(mTvTime.getText().toString()); 151 | System.out.println(mInfo); 152 | EventBus.getDefault().post(mInfo); 153 | finish(); 154 | } 155 | return true; 156 | } 157 | }); 158 | return true; 159 | } 160 | 161 | /** 162 | * 任务内容格式是否符合 163 | */ 164 | private boolean verfyContent() { 165 | if (TextUtils.isEmpty(mEdTitle.getText().toString())) { 166 | //开启AlertDialog提示 167 | showErrorDialog(AppConstantValue.HINT_TITLE); 168 | return false; 169 | } else { 170 | boolean flag = verfyTime(); 171 | return flag; 172 | } 173 | } 174 | 175 | /** 176 | * 创建AlertDialog提示 177 | * 178 | * @param hint 179 | */ 180 | private void showErrorDialog(final String hint) { 181 | AlertDialog.Builder builder = new AlertDialog.Builder(this); 182 | LayoutInflater inflater = LayoutInflater.from(this); 183 | View view = inflater.inflate(R.layout.dialog_view, null); 184 | Button btn_hint = (Button) view.findViewById(R.id.btn_hint); 185 | TextView tv_hint = (TextView) view.findViewById(R.id.tv_hint); 186 | tv_hint.setText(hint); 187 | builder.setView(view, 0, 0, 0, 0); 188 | btn_hint.setOnClickListener(new View.OnClickListener() { 189 | @Override 190 | public void onClick(View v) { 191 | mDialog.dismiss(); 192 | if (hint.equals(AppConstantValue.HINT_TITLE)) { 193 | verfyTime(); 194 | } 195 | } 196 | }); 197 | mDialog = builder.create(); 198 | mDialog.show(); 199 | 200 | 201 | } 202 | 203 | /** 204 | * 检查时间段设置是否正确 205 | */ 206 | private boolean verfyTime() { 207 | String time = mTvTime.getText().toString(); 208 | String[] split = time.split("-"); 209 | String begin = split[0]; 210 | String end = split[1]; 211 | String[] begin_split = begin.split(":"); 212 | String[] end_split = end.split(":"); 213 | int hour_begin = Integer.parseInt(begin_split[0]); 214 | int minute_begin = Integer.parseInt(begin_split[1]); 215 | int hour_end = Integer.parseInt(end_split[0]); 216 | int minute_end = Integer.parseInt(end_split[1]); 217 | SimpleDateFormat format = new SimpleDateFormat("HH:mm"); 218 | String cur_time = format.format(new Date()); 219 | String[] cur_split = cur_time.split(":"); 220 | int hour_cur = Integer.parseInt(cur_split[0]); 221 | int minute_cur = Integer.parseInt(cur_split[1]); 222 | //获取当前的年月日信息 223 | format = new SimpleDateFormat("yyyy-MM-dd"); 224 | String cur_date = format.format(new Date()); 225 | String[] date_split = cur_date.split("-"); 226 | int cur_day = Integer.parseInt(date_split[2]); 227 | String show_date = mTvToday.getText().toString(); 228 | String[] show_split = show_date.split("-"); 229 | int show_day = Integer.parseInt(show_split[2]); 230 | if (show_day == cur_day) { 231 | if (hour_cur > hour_begin) { 232 | showErrorDialog(AppConstantValue.HINT_MISS); 233 | return false; 234 | } else if (hour_cur == hour_begin) { 235 | if (minute_cur > minute_begin) { 236 | showErrorDialog(AppConstantValue.HINT_MISS); 237 | return false; 238 | } 239 | } 240 | } 241 | 242 | if (hour_begin > hour_end) { 243 | showErrorDialog(AppConstantValue.HINT_TIME); 244 | return false; 245 | } else if (hour_begin == hour_end) { 246 | if (minute_begin > minute_end) { 247 | showErrorDialog(AppConstantValue.HINT_TIME); 248 | return false; 249 | } 250 | } 251 | return true; 252 | } 253 | 254 | /** 255 | * 设置具体时间信息 256 | * 257 | * @param ymd 258 | * @param hm 259 | */ 260 | private void setDate(String ymd, String hm) { 261 | //获得当前小时时间 262 | String hour = hm.substring(0, 2); 263 | int cur_hour = Integer.parseInt(hour); 264 | if (cur_hour > 22) { 265 | Date date = new Date();//取时间 266 | Calendar calendar = new GregorianCalendar(); 267 | calendar.setTime(date); 268 | calendar.add(calendar.DATE, 1); 269 | date = calendar.getTime(); 270 | SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd"); 271 | ymd = formatter.format(date); 272 | } 273 | mTvTime.setText(hm + "-" + hm); 274 | mTvToday.setText(ymd); 275 | } 276 | 277 | 278 | @Override 279 | public void onClick(View v) { 280 | Calendar now = Calendar.getInstance(); 281 | TimePickerDialog tpd = TimePickerDialog.newInstance( 282 | EditActivity.this, 283 | now.get(Calendar.HOUR_OF_DAY), 284 | now.get(Calendar.MINUTE), 285 | false); 286 | tpd.setOnCancelListener(new DialogInterface.OnCancelListener() { 287 | @Override 288 | public void onCancel(DialogInterface dialogInterface) { 289 | Log.d("TimePicker", "Dialog was cancelled"); 290 | } 291 | }); 292 | tpd.show(getFragmentManager(), "Timepickerdialog"); 293 | } 294 | 295 | @Override 296 | public void onTimeSet(RadialPickerLayout view, int hourOfDay, int minute, int hourOfDayEnd, int minuteEnd) { 297 | System.out.println("hourOfDay:" + hourOfDay); 298 | System.out.println("minute:" + minute); 299 | System.out.println("hourOfDayEnd:" + hourOfDayEnd); 300 | System.out.println("minuteEnd:" + minuteEnd); 301 | //这个日历库有个BUG 12点与24点显示有误 302 | int temp = hourOfDay; 303 | if (temp == 0) { 304 | hourOfDay = 12; 305 | } 306 | if (temp == 12) { 307 | hourOfDay = 0; 308 | } 309 | String hourString = hourOfDay < 10 ? "0" + hourOfDay : "" + hourOfDay; 310 | String minuteString = minute < 10 ? "0" + minute : "" + minute; 311 | String hourStringEnd = hourOfDayEnd < 10 ? "0" + hourOfDayEnd : "" + hourOfDayEnd; 312 | String minuteStringEnd = minuteEnd < 10 ? "0" + minuteEnd : "" + minuteEnd; 313 | String time = hourString + ":" + minuteString + "-" + hourStringEnd + ":" + minuteStringEnd; 314 | 315 | mTvTime.setText(time); 316 | } 317 | 318 | } 319 | -------------------------------------------------------------------------------- /Timeline/app/src/main/java/com/lvr/timeline/home/ui/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.lvr.timeline.home.ui; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.design.widget.NavigationView; 6 | import android.support.v4.app.Fragment; 7 | import android.support.v4.app.FragmentTransaction; 8 | import android.support.v4.widget.DrawerLayout; 9 | import android.support.v7.app.ActionBar; 10 | import android.support.v7.app.ActionBarDrawerToggle; 11 | import android.support.v7.widget.Toolbar; 12 | import android.view.Menu; 13 | import android.view.MenuItem; 14 | import android.view.View; 15 | import android.widget.FrameLayout; 16 | import android.widget.RadioButton; 17 | import android.widget.RadioGroup; 18 | 19 | import com.lvr.timeline.R; 20 | import com.lvr.timeline.app.AppApplication; 21 | import com.lvr.timeline.base.BaseActivity; 22 | import com.lvr.timeline.home.fragment.CountDownFragment; 23 | import com.lvr.timeline.home.fragment.TodayFragment; 24 | import com.lvr.timeline.home.fragment.YesterdayFragment; 25 | import com.lvr.timeline.utils.StatusBarSetting; 26 | 27 | import java.util.List; 28 | 29 | import butterknife.BindView; 30 | 31 | public class MainActivity extends BaseActivity implements View.OnClickListener{ 32 | 33 | 34 | @BindView(R.id.toolbar) 35 | Toolbar mToolbar; 36 | @BindView(R.id.navigation) 37 | NavigationView mNavigation; 38 | @BindView(R.id.drawer_layout) 39 | DrawerLayout mDrawerLayout; 40 | @BindView(R.id.fl_root) 41 | FrameLayout mFl_root; 42 | @BindView(R.id.rb_today) 43 | RadioButton mRbToday; 44 | @BindView(R.id.rb_time) 45 | RadioButton mRbTime; 46 | @BindView(R.id.rb_yesterday) 47 | RadioButton mRbYesterday; 48 | @BindView(R.id.rg_root) 49 | RadioGroup mRgRoot; 50 | private CountDownFragment mCountDownFragment; 51 | private TodayFragment mTodayFragment; 52 | private YesterdayFragment mYesterdayFragment; 53 | 54 | 55 | 56 | @Override 57 | public int getLayoutId() { 58 | return R.layout.activity_home; 59 | } 60 | 61 | @Override 62 | public void initPresenter() { 63 | 64 | } 65 | 66 | @Override 67 | public void initView() { 68 | StatusBarSetting.setColorForDrawerLayout(this, mDrawerLayout, getResources().getColor(R.color.colorPrimary), StatusBarSetting.DEFAULT_STATUS_BAR_ALPHA); 69 | setToolBar(); 70 | setNavigationView(); 71 | setRadioGroup(); 72 | 73 | } 74 | 75 | private void setRadioGroup() { 76 | mRbToday.setOnClickListener(this); 77 | mRbYesterday.setOnClickListener(this); 78 | mRbTime.setOnClickListener(this); 79 | mRbToday.setChecked(true); 80 | mRbToday.setTextColor(getResources().getColor(R.color.tab_selecet)); 81 | } 82 | 83 | @Override 84 | public void onCreate(Bundle savedInstanceState) { 85 | super.onCreate(savedInstanceState); 86 | //初始化Framgent,考虑到异常杀死的情况 87 | initFragment(savedInstanceState); 88 | } 89 | 90 | private void initFragment(Bundle savedInstanceState) { 91 | if (savedInstanceState != null) { 92 | //异常情况下 93 | List fragments = getSupportFragmentManager().getFragments(); 94 | for (Fragment fragment : fragments) { 95 | if (fragment instanceof TodayFragment) { 96 | mTodayFragment = (TodayFragment) fragment; 97 | } 98 | if (fragment instanceof YesterdayFragment) { 99 | mYesterdayFragment = (YesterdayFragment) fragment; 100 | } 101 | if (fragment instanceof CountDownFragment) { 102 | mCountDownFragment = (CountDownFragment) fragment; 103 | } 104 | } 105 | } else { 106 | //添加到FragmentManger,异常时,自动保存Fragment状态 107 | mTodayFragment = new TodayFragment(); 108 | mYesterdayFragment = new YesterdayFragment(); 109 | mCountDownFragment = new CountDownFragment(); 110 | FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); 111 | transaction.add(R.id.fl_root, mTodayFragment); 112 | transaction.add(R.id.fl_root, mYesterdayFragment); 113 | transaction.add(R.id.fl_root, mCountDownFragment); 114 | transaction.commit(); 115 | } 116 | 117 | //显示与隐藏正确的Fragment 118 | getSupportFragmentManager().beginTransaction(). 119 | show(mTodayFragment). 120 | hide(mYesterdayFragment). 121 | hide(mCountDownFragment).commit(); 122 | } 123 | 124 | 125 | private void setNavigationView() { 126 | //NavigationView初始化 127 | mNavigation.setItemIconTintList(null); 128 | View headerView = mNavigation.getHeaderView(0); 129 | 130 | } 131 | 132 | private void setToolBar() { 133 | mToolbar.setTitle("今天");//设置标题 134 | setSupportActionBar(mToolbar); 135 | ActionBar actionBar = getSupportActionBar(); 136 | //菜单按钮可用 137 | actionBar.setHomeButtonEnabled(true); 138 | //回退按钮可用 139 | actionBar.setDisplayHomeAsUpEnabled(true); 140 | //将drawlayout与toolbar绑定在一起 141 | ActionBarDrawerToggle abdt = new ActionBarDrawerToggle(this, mDrawerLayout, mToolbar, R.string.app_name, R.string.app_name); 142 | abdt.syncState();//初始化状态 143 | //设置drawlayout的监听事件 打开/关闭 144 | mDrawerLayout.setDrawerListener(abdt); 145 | //actionbar中的内容进行初始化 146 | mToolbar.setTitleTextColor(getResources().getColor(R.color.white)); 147 | } 148 | 149 | 150 | @Override 151 | public void onClick(View v) { 152 | switch (v.getId()){ 153 | case(R.id.rb_today):{ 154 | clearState(); 155 | mRbToday.setChecked(true); 156 | mRbToday.setTextColor(getResources().getColor(R.color.tab_selecet)); 157 | getSupportFragmentManager().beginTransaction().show(mTodayFragment) 158 | .hide(mYesterdayFragment) 159 | .hide(mCountDownFragment) 160 | .commit(); 161 | break; 162 | } 163 | case(R.id.rb_time):{ 164 | clearState(); 165 | mRbTime.setChecked(true); 166 | mRbTime.setTextColor(getResources().getColor(R.color.tab_selecet)); 167 | getSupportFragmentManager().beginTransaction().show(mCountDownFragment) 168 | .hide(mYesterdayFragment) 169 | .hide(mTodayFragment) 170 | .commit(); 171 | break; 172 | } 173 | case(R.id.rb_yesterday):{ 174 | clearState(); 175 | mRbYesterday.setChecked(true); 176 | mRbYesterday.setTextColor(getResources().getColor(R.color.tab_selecet)); 177 | getSupportFragmentManager().beginTransaction().show(mYesterdayFragment) 178 | .hide(mTodayFragment) 179 | .hide(mCountDownFragment) 180 | .commit(); 181 | break; 182 | } 183 | } 184 | } 185 | 186 | //清空之前的状态 187 | private void clearState() { 188 | mRgRoot.clearCheck(); 189 | mRbTime.setTextColor(getResources().getColor(R.color.black)); 190 | mRbToday.setTextColor(getResources().getColor(R.color.black)); 191 | mRbYesterday.setTextColor(getResources().getColor(R.color.black)); 192 | 193 | } 194 | @Override 195 | public boolean onCreateOptionsMenu(Menu menu) { 196 | getMenuInflater().inflate(R.menu.menu_toolabr, menu); 197 | MenuItem item = menu.findItem(R.id.mn_it_edit); 198 | item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { 199 | @Override 200 | public boolean onMenuItemClick(MenuItem item) { 201 | Intent intent = new Intent(getApplicationContext(),EditActivity.class); 202 | startActivity(intent); 203 | return true; 204 | } 205 | }); 206 | return true; 207 | } 208 | 209 | @Override 210 | protected void onDestroy() { 211 | super.onDestroy(); 212 | AppApplication.REALM_INSTANCE.close(); 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /Timeline/app/src/main/java/com/lvr/timeline/utils/CollectionUtils.java: -------------------------------------------------------------------------------- 1 | package com.lvr.timeline.utils; 2 | 3 | import java.util.Collection; 4 | 5 | /** 6 | * 集合操作工具类 7 | * 8 | */ 9 | public class CollectionUtils { 10 | 11 | /** 12 | * 判断集合是否为null或者0个元素 13 | * 14 | * @param c 15 | * @return 16 | */ 17 | public static boolean isNullOrEmpty(Collection c) { 18 | if (null == c || c.isEmpty()) { 19 | return true; 20 | } 21 | return false; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Timeline/app/src/main/java/com/lvr/timeline/utils/DisplayUtil.java: -------------------------------------------------------------------------------- 1 | package com.lvr.timeline.utils; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.graphics.Bitmap; 6 | import android.graphics.Rect; 7 | import android.util.DisplayMetrics; 8 | import android.view.View; 9 | import android.view.ViewTreeObserver; 10 | import android.view.ViewTreeObserver.OnGlobalLayoutListener; 11 | import android.view.WindowManager; 12 | import android.widget.LinearLayout; 13 | 14 | import com.lvr.timeline.app.AppApplication; 15 | 16 | 17 | /** 18 | * 屏幕相关的辅助类 19 | */ 20 | public class DisplayUtil { 21 | private DisplayUtil() { 22 | /* cannot be instantiated */ 23 | throw new UnsupportedOperationException("cannot be instantiated"); 24 | } 25 | 26 | 27 | /** 28 | * 将px值转换为dip或dp值,保证尺寸大小不变 29 | * 30 | * @param pxValue 31 | * (DisplayMetrics类中属性density) 32 | * @return 33 | */ 34 | public static int px2dip(float pxValue) { 35 | final float scale = AppApplication.getAppContext().getResources().getDisplayMetrics().density; 36 | return (int) (pxValue / scale + 0.5f); 37 | } 38 | 39 | /** 40 | * 将dip或dp值转换为px值,保证尺寸大小不变 41 | * 42 | * @param dipValue 43 | * (DisplayMetrics类中属性density) 44 | * @return 45 | */ 46 | public static int dip2px( float dipValue) { 47 | final float scale = AppApplication.getAppContext().getResources().getDisplayMetrics().density; 48 | return (int) (dipValue * scale + 0.5f); 49 | } 50 | 51 | /** 52 | * 将px值转换为sp值,保证文字大小不变 53 | * 54 | * @param pxValue 55 | * (DisplayMetrics类中属性scaledDensity) 56 | * @return 57 | */ 58 | public static int px2sp(float pxValue) { 59 | final float fontScale = AppApplication.getAppContext().getResources().getDisplayMetrics().scaledDensity; 60 | return (int) (pxValue / fontScale + 0.5f); 61 | } 62 | 63 | /** 64 | * 将sp值转换为px值,保证文字大小不变 65 | * 66 | * @param spValue 67 | * (DisplayMetrics类中属性scaledDensity) 68 | * @return 69 | */ 70 | public static int sp2px(float spValue) { 71 | final float fontScale = AppApplication.getAppContext().getResources().getDisplayMetrics().scaledDensity; 72 | return (int) (spValue * fontScale + 0.5f); 73 | } 74 | 75 | /** 76 | * 直接获取控件的宽、高 77 | * @param view 78 | * @return int[] 79 | */ 80 | public static int[] getWidgetWH(final View view){ 81 | ViewTreeObserver vto2 = view.getViewTreeObserver(); 82 | vto2.addOnGlobalLayoutListener(new OnGlobalLayoutListener() { 83 | @Override 84 | public void onGlobalLayout() { 85 | view.getViewTreeObserver().removeGlobalOnLayoutListener(this); 86 | } 87 | }); 88 | return new int[]{view.getWidth(),view.getHeight()}; 89 | } 90 | 91 | /** 92 | * 直接获取控件的宽、高 93 | * @param view 94 | * @return int[] 95 | */ 96 | public static int getViewHeight(final View view){ 97 | ViewTreeObserver vto2 = view.getViewTreeObserver(); 98 | vto2.addOnGlobalLayoutListener(new OnGlobalLayoutListener() { 99 | @Override 100 | public void onGlobalLayout() { 101 | view.getViewTreeObserver().removeGlobalOnLayoutListener(this); 102 | } 103 | }); 104 | return view.getHeight(); 105 | } 106 | 107 | /** 108 | * 直接获取控件的宽、高 109 | * @param view 110 | * @return int[] 111 | */ 112 | public static int getViewWidth(final View view){ 113 | ViewTreeObserver vto2 = view.getViewTreeObserver(); 114 | vto2.addOnGlobalLayoutListener(new OnGlobalLayoutListener() { 115 | @Override 116 | public void onGlobalLayout() { 117 | view.getViewTreeObserver().removeGlobalOnLayoutListener(this); 118 | } 119 | }); 120 | return view.getWidth(); 121 | } 122 | 123 | /** 124 | * 获得屏幕宽度 125 | * 126 | * @param context 127 | * @return 128 | */ 129 | public static int getScreenWidth(Context context) { 130 | WindowManager wm = (WindowManager) context 131 | .getSystemService(Context.WINDOW_SERVICE); 132 | DisplayMetrics outMetrics = new DisplayMetrics(); 133 | wm.getDefaultDisplay().getMetrics(outMetrics); 134 | return outMetrics.widthPixels; 135 | } 136 | 137 | /** 138 | * 获得屏幕高度 139 | * 140 | * @param context 141 | * @return 142 | */ 143 | public static int getScreenHeight(Context context) { 144 | WindowManager wm = (WindowManager) context 145 | .getSystemService(Context.WINDOW_SERVICE); 146 | DisplayMetrics outMetrics = new DisplayMetrics(); 147 | wm.getDefaultDisplay().getMetrics(outMetrics); 148 | return outMetrics.heightPixels; 149 | } 150 | 151 | /** 152 | * 获得状态栏的高度 153 | * 注意:该方法只能在Activity类中使用,在测试模式下失败 154 | * @param context 155 | * @return 156 | */ 157 | public static int getStatusBarHeight(Context context) { 158 | int statusBarHeight = -1; 159 | try { 160 | Class clazz = Class.forName("com.android.internal.R$dimen"); 161 | Object object = clazz.newInstance(); 162 | int height = Integer.parseInt(clazz.getField("status_bar_height") 163 | .get(object).toString()); 164 | statusBarHeight = context.getResources().getDimensionPixelSize(height); 165 | } catch (Exception e) { 166 | e.printStackTrace(); 167 | } 168 | return statusBarHeight; 169 | } 170 | 171 | /** 172 | * 获取控件的宽 173 | * @param view 174 | * @return 175 | */ 176 | public static int getWidgetWidth(View view){ 177 | int w = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); 178 | int h = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); 179 | view.measure(w, h);//先度量 180 | int width = view.getMeasuredWidth(); 181 | return width; 182 | } 183 | /** 184 | * 获取控件的高 185 | * @param view 186 | * @return 187 | */ 188 | public static int getWidgetHeight(View view){ 189 | int w = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); 190 | int h = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); 191 | view.measure(w, h);//先度量 192 | int height = view.getMeasuredHeight(); 193 | return height; 194 | } 195 | /** 196 | * 设置控件宽 197 | * @param view 198 | * @param width 199 | */ 200 | public static void setWidgetWidth(View view, int width){ 201 | LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) view.getLayoutParams(); 202 | params.width = width; 203 | view.setLayoutParams(params); 204 | } 205 | /** 206 | * 设置控件高 207 | * @param view 208 | * @param height 209 | */ 210 | public static void setWidgetHeight(View view, int height){ 211 | LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) view.getLayoutParams(); 212 | params.height = height; 213 | view.setLayoutParams(params); 214 | } 215 | 216 | 217 | //---------------------------------------------- 218 | 219 | /** 220 | * 获取当前屏幕截图,包含状态栏(这个方法没测试通过) 221 | * 222 | * @param activity 223 | * @return Bitmap 224 | */ 225 | public static Bitmap snapShotWithStatusBar(Activity activity) { 226 | View view = activity.getWindow().getDecorView(); 227 | view.setDrawingCacheEnabled(true); 228 | view.buildDrawingCache(); 229 | Bitmap bmp = view.getDrawingCache(); 230 | int width = getScreenWidth(activity); 231 | int height = getScreenHeight(activity); 232 | Bitmap bp = null; 233 | bp = Bitmap.createBitmap(bmp, 0, 0, width, height); 234 | view.destroyDrawingCache(); 235 | return bp; 236 | } 237 | 238 | /** 239 | * 获取当前屏幕截图,不包含状态栏(这个方法没测试通过) 240 | * 241 | * @param activity 242 | * @return Bitmap 243 | */ 244 | public static Bitmap snapShotWithoutStatusBar(Activity activity) { 245 | View view = activity.getWindow().getDecorView(); 246 | view.setDrawingCacheEnabled(true); 247 | view.buildDrawingCache(); 248 | Bitmap bmp = view.getDrawingCache(); 249 | Rect frame = new Rect(); 250 | activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(frame); 251 | int statusBarHeight = frame.top; 252 | 253 | int width = getScreenWidth(activity); 254 | int height = getScreenHeight(activity); 255 | Bitmap bp = null; 256 | bp = Bitmap.createBitmap(bmp, 0, statusBarHeight, width, height 257 | - statusBarHeight); 258 | view.destroyDrawingCache(); 259 | return bp; 260 | } 261 | } 262 | -------------------------------------------------------------------------------- /Timeline/app/src/main/java/com/lvr/timeline/utils/GlideRoundTransformUtil.java: -------------------------------------------------------------------------------- 1 | package com.lvr.timeline.utils; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.graphics.BitmapShader; 6 | import android.graphics.Canvas; 7 | import android.graphics.Paint; 8 | 9 | import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool; 10 | import com.bumptech.glide.load.resource.bitmap.BitmapTransformation; 11 | 12 | 13 | /** 14 | * description:glide转换圆角图片 15 | * Created by xsf 16 | * on 2016.04.15:17 17 | */ 18 | public class GlideRoundTransformUtil extends BitmapTransformation { 19 | public GlideRoundTransformUtil(Context context) { 20 | super(context); 21 | } 22 | 23 | @Override 24 | protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) { 25 | return circleCrop(pool, toTransform); 26 | } 27 | 28 | private static Bitmap circleCrop(BitmapPool pool, Bitmap source) { 29 | if (source == null) return null; 30 | 31 | int size = Math.min(source.getWidth(), source.getHeight()); 32 | int x = (source.getWidth() - size) / 2; 33 | int y = (source.getHeight() - size) / 2; 34 | 35 | // TODO this could be acquired from the pool too 36 | Bitmap squared = Bitmap.createBitmap(source, x, y, size, size); 37 | 38 | Bitmap result = pool.get(size, size, Bitmap.Config.ARGB_8888); 39 | if (result == null) { 40 | result = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); 41 | } 42 | 43 | Canvas canvas = new Canvas(result); 44 | Paint paint = new Paint(); 45 | paint.setShader(new BitmapShader(squared, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP)); 46 | paint.setAntiAlias(true); 47 | float r = size / 2f; 48 | canvas.drawCircle(r, r, r, paint); 49 | return result; 50 | } 51 | 52 | @Override 53 | public String getId() { 54 | return getClass().getName(); 55 | } 56 | } -------------------------------------------------------------------------------- /Timeline/app/src/main/java/com/lvr/timeline/utils/ImageLoaderUtils.java: -------------------------------------------------------------------------------- 1 | package com.lvr.timeline.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.DecodeFormat; 8 | import com.bumptech.glide.load.engine.DiskCacheStrategy; 9 | import com.lvr.timeline.R; 10 | 11 | import java.io.File; 12 | 13 | /** 14 | * Description : 图片加载工具类 使用glide框架封装 15 | */ 16 | public class ImageLoaderUtils { 17 | 18 | public static void display(Context context, ImageView imageView, String url, int placeholder, int error) { 19 | if (imageView == null) { 20 | throw new IllegalArgumentException("argument error"); 21 | } 22 | Glide.with(context).load(url).placeholder(placeholder) 23 | .error(error).crossFade().into(imageView); 24 | } 25 | 26 | public static void display(Context context, ImageView imageView, String url) { 27 | if (imageView == null) { 28 | throw new IllegalArgumentException("argument error"); 29 | } 30 | Glide.with(context).load(url) 31 | .diskCacheStrategy(DiskCacheStrategy.ALL) 32 | .centerCrop() 33 | .placeholder(R.drawable.ic_image_loading) 34 | .error(R.drawable.ic_empty_picture) 35 | .crossFade().into(imageView); 36 | } 37 | 38 | public static void display(Context context, ImageView imageView, File url) { 39 | if (imageView == null) { 40 | throw new IllegalArgumentException("argument error"); 41 | } 42 | Glide.with(context).load(url) 43 | .diskCacheStrategy(DiskCacheStrategy.ALL) 44 | .centerCrop() 45 | .placeholder(R.drawable.ic_image_loading) 46 | .error(R.drawable.ic_empty_picture) 47 | .crossFade().into(imageView); 48 | } 49 | public static void displaySmallPhoto(Context context, ImageView imageView, String url) { 50 | if (imageView == null) { 51 | throw new IllegalArgumentException("argument error"); 52 | } 53 | Glide.with(context).load(url).asBitmap() 54 | .diskCacheStrategy(DiskCacheStrategy.ALL) 55 | .placeholder(R.drawable.ic_image_loading) 56 | .error(R.drawable.ic_empty_picture) 57 | .thumbnail(0.5f) 58 | .into(imageView); 59 | } 60 | public static void displayBigPhoto(Context context, ImageView imageView, String url) { 61 | if (imageView == null) { 62 | throw new IllegalArgumentException("argument error"); 63 | } 64 | Glide.with(context).load(url).asBitmap() 65 | .format(DecodeFormat.PREFER_ARGB_8888) 66 | .diskCacheStrategy(DiskCacheStrategy.ALL) 67 | .placeholder(R.drawable.ic_image_loading) 68 | .error(R.drawable.ic_empty_picture) 69 | .into(imageView); 70 | } 71 | public static void display(Context context, ImageView imageView, int url) { 72 | if (imageView == null) { 73 | throw new IllegalArgumentException("argument error"); 74 | } 75 | Glide.with(context).load(url) 76 | .diskCacheStrategy(DiskCacheStrategy.ALL) 77 | .centerCrop() 78 | .placeholder(R.drawable.ic_image_loading) 79 | .error(R.drawable.ic_empty_picture) 80 | .crossFade().into(imageView); 81 | } 82 | public static void displayRound(Context context, ImageView imageView, String url) { 83 | if (imageView == null) { 84 | throw new IllegalArgumentException("argument error"); 85 | } 86 | Glide.with(context).load(url) 87 | .diskCacheStrategy(DiskCacheStrategy.ALL) 88 | .error(R.drawable.toux2) 89 | .centerCrop().transform(new GlideRoundTransformUtil(context)).into(imageView); 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /Timeline/app/src/main/java/com/lvr/timeline/utils/RealmOperationHelper.java: -------------------------------------------------------------------------------- 1 | package com.lvr.timeline.utils; 2 | 3 | import java.lang.reflect.InvocationTargetException; 4 | import java.lang.reflect.Method; 5 | import java.util.List; 6 | 7 | import io.realm.Realm; 8 | import io.realm.RealmAsyncTask; 9 | import io.realm.RealmObject; 10 | import io.realm.RealmResults; 11 | import io.realm.Sort; 12 | 13 | /** 14 | * Created by lvr on 2017/4/20. 15 | */ 16 | 17 | public class RealmOperationHelper { 18 | private static Realm mRealm; 19 | 20 | private static class SingletonHolder { 21 | private static RealmOperationHelper INSTANCE = new RealmOperationHelper( 22 | mRealm); 23 | } 24 | 25 | private RealmOperationHelper(Realm realm) { 26 | this.mRealm = realm; 27 | 28 | } 29 | 30 | /** 31 | * 获取RealmOperation的单例 32 | * 33 | * @param realm 传入realm实例对象 34 | * @return 返回RealmOperation的单例 35 | */ 36 | public static RealmOperationHelper getInstance(Realm realm) { 37 | if (realm != null) { 38 | mRealm = realm; 39 | } 40 | return SingletonHolder.INSTANCE; 41 | } 42 | 43 | /** 44 | * 增加单条数据到数据库中 45 | * 46 | * @param bean 数据对象,必须继承了RealmObject 47 | */ 48 | public void add(final RealmObject bean) { 49 | mRealm.executeTransaction(new Realm.Transaction() { 50 | @Override 51 | public void execute(Realm realm) { 52 | 53 | realm.copyToRealm(bean); 54 | 55 | } 56 | }); 57 | 58 | } 59 | 60 | /** 61 | * 增加多条数据到数据库中 62 | * 63 | * @param beans 数据集合,其中元素必须继承了RealmObject 64 | */ 65 | public void add(final List beans) { 66 | mRealm.executeTransaction(new Realm.Transaction() { 67 | @Override 68 | public void execute(Realm realm) { 69 | realm.copyToRealm(beans); 70 | 71 | } 72 | }); 73 | 74 | } 75 | /** 76 | * 增加多条数据到数据库中 77 | * 78 | * @param beans 数据集合,其中元素必须继承了RealmObject 79 | */ 80 | public void addAsync(final List beans) { 81 | mRealm.executeTransactionAsync(new Realm.Transaction() { 82 | @Override 83 | public void execute(Realm realm) { 84 | realm.copyToRealm(beans); 85 | } 86 | }); 87 | 88 | } 89 | 90 | /** 91 | * 删除数据库中clazz类所属所有元素 92 | * 93 | * @param clazz 94 | */ 95 | public void deleteAll(Class clazz) { 96 | final RealmResults beans = mRealm.where(clazz).findAll(); 97 | 98 | mRealm.executeTransaction(new Realm.Transaction() { 99 | @Override 100 | public void execute(Realm realm) { 101 | beans.deleteAllFromRealm(); 102 | 103 | } 104 | }); 105 | 106 | } 107 | /** 108 | * 删除数据库中clazz类所属所有元素 109 | * 110 | * @param clazz 111 | */ 112 | public void deleteAllAsync(Class clazz) { 113 | final RealmResults beans = mRealm.where(clazz).findAll(); 114 | 115 | mRealm.executeTransactionAsync(new Realm.Transaction() { 116 | @Override 117 | public void execute(Realm realm) { 118 | beans.deleteAllFromRealm(); 119 | 120 | } 121 | }); 122 | 123 | 124 | } 125 | 126 | /** 127 | * 删除数据库中clazz类所属第一个元素 128 | * 129 | * @param clazz 130 | */ 131 | public void deleteFirst(Class clazz) { 132 | final RealmResults beans = mRealm.where(clazz).findAll(); 133 | 134 | mRealm.executeTransaction(new Realm.Transaction() { 135 | @Override 136 | public void execute(Realm realm) { 137 | beans.deleteFirstFromRealm(); 138 | 139 | } 140 | }); 141 | 142 | } 143 | 144 | /** 145 | * 删除数据库中clazz类所属最后一个元素 146 | * 147 | * @param clazz 148 | */ 149 | public void deleteLast(Class clazz) { 150 | final RealmResults beans = mRealm.where(clazz).findAll(); 151 | 152 | mRealm.executeTransaction(new Realm.Transaction() { 153 | @Override 154 | public void execute(Realm realm) { 155 | beans.deleteLastFromRealm(); 156 | 157 | } 158 | }); 159 | 160 | } 161 | 162 | /** 163 | * 删除数据库中clazz类所属数据中某一位置的元素 164 | * 165 | * @param clazz 166 | * @param position 167 | */ 168 | public void deleteElement(Class clazz, final int position) { 169 | final RealmResults beans = mRealm.where(clazz).findAll(); 170 | 171 | mRealm.executeTransaction(new Realm.Transaction() { 172 | @Override 173 | public void execute(Realm realm) { 174 | beans.deleteFromRealm(position); 175 | 176 | } 177 | }); 178 | 179 | } 180 | 181 | /** 182 | * 查询数据库中clazz类所属所有数据 183 | * 184 | * @param clazz 185 | * @return 186 | */ 187 | public RealmResults queryAll(Class clazz) { 188 | final RealmResults beans = mRealm.where(clazz).findAll(); 189 | 190 | return beans; 191 | } 192 | /** 193 | * 查询数据库中clazz类所属所有数据 194 | * 195 | * @param clazz 196 | * @return 197 | */ 198 | public RealmResults queryAllAsync(Class clazz) { 199 | final RealmResults beans = mRealm.where(clazz).findAllAsync(); 200 | 201 | return beans; 202 | } 203 | 204 | /** 205 | * 查询满足条件的第一个数据 206 | * 207 | * @param clazz 208 | * @param fieldName 209 | * @param value 210 | * @return 211 | * @throws NoSuchFieldException 212 | */ 213 | public RealmObject queryByFieldFirst(Class clazz, String fieldName, String value) throws NoSuchFieldException { 214 | 215 | final RealmObject bean = mRealm.where(clazz).equalTo(fieldName, value).findFirst(); 216 | 217 | return bean; 218 | } 219 | 220 | /** 221 | * 查询满足条件的所有数据 222 | * 223 | * @param clazz 224 | * @param fieldName 225 | * @param value 226 | * @return 227 | * @throws NoSuchFieldException 228 | */ 229 | public RealmResults queryByFieldAll(Class clazz, String fieldName, String value) throws NoSuchFieldException { 230 | 231 | final RealmResults beans = mRealm.where(clazz).equalTo(fieldName, value).findAll(); 232 | 233 | return beans; 234 | } 235 | /** 236 | * 查询满足条件的所有数据 237 | * 238 | * @param clazz 239 | * @param fieldName 240 | * @param value 241 | * @return 242 | * @throws NoSuchFieldException 243 | */ 244 | public RealmResults queryByFieldAllAsync(Class clazz, String fieldName, String value) throws NoSuchFieldException { 245 | 246 | final RealmResults beans = mRealm.where(clazz).equalTo(fieldName, value).findAllAsync(); 247 | 248 | return beans; 249 | } 250 | 251 | /** 252 | * 查询满足条件的第一个数据 253 | * 254 | * @param clazz 255 | * @param fieldName 256 | * @param value 257 | * @return 258 | * @throws NoSuchFieldException 259 | */ 260 | public RealmObject queryByFieldFirst(Class clazz, String fieldName, int value) throws NoSuchFieldException { 261 | final RealmObject bean = mRealm.where(clazz).equalTo(fieldName, value).findFirst(); 262 | 263 | return bean; 264 | } 265 | 266 | /** 267 | * 查询满足条件的所有数据 268 | * 269 | * @param clazz 270 | * @param fieldName 271 | * @param value 272 | * @return 273 | * @throws NoSuchFieldException 274 | */ 275 | public RealmResults queryByFieldAll(Class clazz, String fieldName, int value) throws NoSuchFieldException { 276 | final RealmResults beans = mRealm.where(clazz).equalTo(fieldName, value).findAll(); 277 | return beans; 278 | } 279 | /** 280 | * 查询满足条件的所有数据 281 | * 282 | * @param clazz 283 | * @param fieldName 284 | * @param value 285 | * @return 286 | * @throws NoSuchFieldException 287 | */ 288 | public RealmResults queryByFieldAllAsync(Class clazz, String fieldName, int value) throws NoSuchFieldException { 289 | 290 | final RealmResults beans = mRealm.where(clazz).equalTo(fieldName, value).findAllAsync(); 291 | return beans; 292 | } 293 | 294 | /** 295 | * 查询数据,按增量排序 296 | * 297 | * @param clazz 298 | * @param fieldName 299 | * @return 300 | */ 301 | public List queryAllByAscending(Class clazz, String fieldName) { 302 | RealmResults beans = mRealm.where(clazz).findAll(); 303 | RealmResults results = beans.sort(fieldName, Sort.ASCENDING); 304 | return mRealm.copyFromRealm(results); 305 | } 306 | 307 | 308 | /** 309 | * 查询数据,按降量排序 310 | * 311 | * @param clazz 312 | * @param fieldName 313 | * @return 314 | */ 315 | public List queryAllByDescending(Class clazz, String fieldName) { 316 | RealmResults beans = mRealm.where(clazz).findAll(); 317 | RealmResults results = beans.sort(fieldName, Sort.DESCENDING); 318 | return mRealm.copyFromRealm(results); 319 | } 320 | 321 | /** 322 | * 更新满足某个条件的第一个数据的属性值 323 | * @param clazz 324 | * @param fieldName 325 | * @param oldValue 326 | * @param newValue 327 | * @throws NoSuchMethodException 328 | * @throws InvocationTargetException 329 | * @throws IllegalAccessException 330 | */ 331 | public void updateFirstByField(Class clazz, String fieldName,String oldValue,String newValue) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { 332 | final RealmObject bean = mRealm.where(clazz).equalTo(fieldName, oldValue).findFirst(); 333 | mRealm.beginTransaction(); 334 | Method method = clazz.getMethod(fieldName, String.class); 335 | method.invoke(bean,newValue); 336 | mRealm.commitTransaction(); 337 | 338 | } 339 | /** 340 | * 更新满足某个条件的第一个数据的属性值 341 | * @param clazz 342 | * @param fieldName 343 | * @param oldValue 344 | * @param newValue 345 | * @throws NoSuchMethodException 346 | * @throws InvocationTargetException 347 | * @throws IllegalAccessException 348 | */ 349 | public void updateFirstByField(Class clazz, String fieldName,int oldValue,int newValue) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { 350 | final RealmObject bean = mRealm.where(clazz).equalTo(fieldName, oldValue).findFirst(); 351 | mRealm.beginTransaction(); 352 | Method method = clazz.getMethod(fieldName, int.class); 353 | method.invoke(bean,newValue); 354 | mRealm.commitTransaction(); 355 | 356 | } 357 | /** 358 | * 更新满足某个条件的第一个数据的属性值 359 | * @param clazz 360 | * @param fieldName 361 | * @param oldValue 362 | * @param newValue 363 | * @throws NoSuchMethodException 364 | * @throws InvocationTargetException 365 | * @throws IllegalAccessException 366 | */ 367 | public void updateAllByField(Class clazz, String fieldName,String oldValue,String newValue) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { 368 | final RealmResults beans = mRealm.where(clazz).equalTo(fieldName, oldValue).findAll(); 369 | mRealm.beginTransaction(); 370 | Method method = clazz.getMethod(fieldName, String.class); 371 | for(int i=0;i clazz, String fieldName,int oldValue,int newValue) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { 389 | final RealmResults beans = mRealm.where(clazz).equalTo(fieldName, oldValue).findAll(); 390 | mRealm.beginTransaction(); 391 | Method method = clazz.getMethod(fieldName, int.class); 392 | for(int i=0;i= Build.VERSION_CODES.LOLLIPOP) { 49 | activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); 50 | activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); 51 | activity.getWindow().setStatusBarColor(calculateStatusColor(color, statusBarAlpha)); 52 | } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 53 | activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); 54 | ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView(); 55 | View fakeStatusBarView = decorView.findViewById(FAKE_STATUS_BAR_VIEW_ID); 56 | if (fakeStatusBarView != null) { 57 | if (fakeStatusBarView.getVisibility() == View.GONE) { 58 | fakeStatusBarView.setVisibility(View.VISIBLE); 59 | } 60 | fakeStatusBarView.setBackgroundColor(calculateStatusColor(color, statusBarAlpha)); 61 | } else { 62 | decorView.addView(createStatusBarView(activity, color, statusBarAlpha)); 63 | } 64 | setRootView(activity); 65 | } 66 | } 67 | 68 | /** 69 | * 为滑动返回界面设置状态栏颜色 70 | * 71 | * @param activity 需要设置的activity 72 | * @param color 状态栏颜色值 73 | */ 74 | public static void setColorForSwipeBack(Activity activity, int color) { 75 | setColorForSwipeBack(activity, color, DEFAULT_STATUS_BAR_ALPHA); 76 | } 77 | 78 | /** 79 | * 为滑动返回界面设置状态栏颜色 80 | * 81 | * @param activity 需要设置的activity 82 | * @param color 状态栏颜色值 83 | * @param statusBarAlpha 状态栏透明度 84 | */ 85 | public static void setColorForSwipeBack(Activity activity, @ColorInt int color, int statusBarAlpha) { 86 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 87 | 88 | ViewGroup contentView = ((ViewGroup) activity.findViewById(android.R.id.content)); 89 | View rootView = contentView.getChildAt(0); 90 | int statusBarHeight = getStatusBarHeight(activity); 91 | if (rootView != null && rootView instanceof CoordinatorLayout) { 92 | final CoordinatorLayout coordinatorLayout = (CoordinatorLayout) rootView; 93 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { 94 | coordinatorLayout.setFitsSystemWindows(false); 95 | contentView.setBackgroundColor(calculateStatusColor(color, statusBarAlpha)); 96 | boolean isNeedRequestLayout = contentView.getPaddingTop() < statusBarHeight; 97 | if (isNeedRequestLayout) { 98 | contentView.setPadding(0, statusBarHeight, 0, 0); 99 | coordinatorLayout.post(new Runnable() { 100 | @Override 101 | public void run() { 102 | coordinatorLayout.requestLayout(); 103 | } 104 | }); 105 | } 106 | } else { 107 | coordinatorLayout.setStatusBarBackgroundColor(calculateStatusColor(color, statusBarAlpha)); 108 | } 109 | } else { 110 | contentView.setPadding(0, statusBarHeight, 0, 0); 111 | contentView.setBackgroundColor(calculateStatusColor(color, statusBarAlpha)); 112 | } 113 | setTransparentForWindow(activity); 114 | } 115 | } 116 | 117 | /** 118 | * 设置状态栏纯色 不加半透明效果 119 | * 120 | * @param activity 需要设置的 activity 121 | * @param color 状态栏颜色值 122 | */ 123 | public static void setColorNoTranslucent(Activity activity, @ColorInt int color) { 124 | setColor(activity, color, 0); 125 | } 126 | 127 | /** 128 | * 设置状态栏颜色(5.0以下无半透明效果,不建议使用) 129 | * 130 | * @param activity 需要设置的 activity 131 | * @param color 状态栏颜色值 132 | */ 133 | @Deprecated 134 | public static void setColorDiff(Activity activity, @ColorInt int color) { 135 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { 136 | return; 137 | } 138 | transparentStatusBar(activity); 139 | ViewGroup contentView = (ViewGroup) activity.findViewById(android.R.id.content); 140 | // 移除半透明矩形,以免叠加 141 | View fakeStatusBarView = contentView.findViewById(FAKE_STATUS_BAR_VIEW_ID); 142 | if (fakeStatusBarView != null) { 143 | if (fakeStatusBarView.getVisibility() == View.GONE) { 144 | fakeStatusBarView.setVisibility(View.VISIBLE); 145 | } 146 | fakeStatusBarView.setBackgroundColor(color); 147 | } else { 148 | contentView.addView(createStatusBarView(activity, color)); 149 | } 150 | setRootView(activity); 151 | } 152 | 153 | /** 154 | * 使状态栏半透明 155 | * 156 | * 适用于图片作为背景的界面,此时需要图片填充到状态栏 157 | * 158 | * @param activity 需要设置的activity 159 | */ 160 | public static void setTranslucent(Activity activity) { 161 | setTranslucent(activity, DEFAULT_STATUS_BAR_ALPHA); 162 | } 163 | 164 | /** 165 | * 使状态栏半透明 166 | * 167 | * 适用于图片作为背景的界面,此时需要图片填充到状态栏 168 | * 169 | * @param activity 需要设置的activity 170 | * @param statusBarAlpha 状态栏透明度 171 | */ 172 | public static void setTranslucent(Activity activity, int statusBarAlpha) { 173 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { 174 | return; 175 | } 176 | setTransparent(activity); 177 | addTranslucentView(activity, statusBarAlpha); 178 | } 179 | 180 | /** 181 | * 针对根布局是 CoordinatorLayout, 使状态栏半透明 182 | * 183 | * 适用于图片作为背景的界面,此时需要图片填充到状态栏 184 | * 185 | * @param activity 需要设置的activity 186 | * @param statusBarAlpha 状态栏透明度 187 | */ 188 | public static void setTranslucentForCoordinatorLayout(Activity activity, int statusBarAlpha) { 189 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { 190 | return; 191 | } 192 | transparentStatusBar(activity); 193 | addTranslucentView(activity, statusBarAlpha); 194 | } 195 | 196 | /** 197 | * 设置状态栏全透明 198 | * 199 | * @param activity 需要设置的activity 200 | */ 201 | public static void setTransparent(Activity activity) { 202 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { 203 | return; 204 | } 205 | transparentStatusBar(activity); 206 | setRootView(activity); 207 | } 208 | 209 | /** 210 | * 使状态栏透明(5.0以上半透明效果,不建议使用) 211 | * 212 | * 适用于图片作为背景的界面,此时需要图片填充到状态栏 213 | * 214 | * @param activity 需要设置的activity 215 | */ 216 | @Deprecated 217 | public static void setTranslucentDiff(Activity activity) { 218 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 219 | // 设置状态栏透明 220 | activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); 221 | setRootView(activity); 222 | } 223 | } 224 | 225 | /** 226 | * 为DrawerLayout 布局设置状态栏变色 227 | * 228 | * @param activity 需要设置的activity 229 | * @param drawerLayout DrawerLayout 230 | * @param color 状态栏颜色值 231 | */ 232 | public static void setColorForDrawerLayout(Activity activity, DrawerLayout drawerLayout, @ColorInt int color) { 233 | setColorForDrawerLayout(activity, drawerLayout, color, DEFAULT_STATUS_BAR_ALPHA); 234 | } 235 | 236 | /** 237 | * 为DrawerLayout 布局设置状态栏颜色,纯色 238 | * 239 | * @param activity 需要设置的activity 240 | * @param drawerLayout DrawerLayout 241 | * @param color 状态栏颜色值 242 | */ 243 | public static void setColorNoTranslucentForDrawerLayout(Activity activity, DrawerLayout drawerLayout, @ColorInt int color) { 244 | setColorForDrawerLayout(activity, drawerLayout, color, 0); 245 | } 246 | 247 | /** 248 | * 为DrawerLayout 布局设置状态栏变色 249 | * 250 | * @param activity 需要设置的activity 251 | * @param drawerLayout DrawerLayout 252 | * @param color 状态栏颜色值 253 | * @param statusBarAlpha 状态栏透明度 254 | */ 255 | public static void setColorForDrawerLayout(Activity activity, DrawerLayout drawerLayout, @ColorInt int color, 256 | int statusBarAlpha) { 257 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { 258 | return; 259 | } 260 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 261 | activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); 262 | activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); 263 | activity.getWindow().setStatusBarColor(calculateStatusColor(color, statusBarAlpha)); 264 | } else { 265 | activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); 266 | // 生成一个状态栏大小的矩形 267 | // 添加 statusBarView 到布局中 268 | ViewGroup contentLayout = (ViewGroup) drawerLayout.getChildAt(0); 269 | View fakeStatusBarView = contentLayout.findViewById(FAKE_STATUS_BAR_VIEW_ID); 270 | if (fakeStatusBarView != null) { 271 | if (fakeStatusBarView.getVisibility() == View.GONE) { 272 | fakeStatusBarView.setVisibility(View.VISIBLE); 273 | } 274 | fakeStatusBarView.setBackgroundColor(color); 275 | } else { 276 | contentLayout.addView(createStatusBarView(activity, color), 0); 277 | } 278 | // 内容布局不是 LinearLayout 时,设置padding top 279 | if (!(contentLayout instanceof LinearLayout) && contentLayout.getChildAt(1) != null) { 280 | contentLayout.getChildAt(1) 281 | .setPadding(contentLayout.getPaddingLeft(), getStatusBarHeight(activity) + contentLayout.getPaddingTop(), 282 | contentLayout.getPaddingRight(), contentLayout.getPaddingBottom()); 283 | } 284 | // 设置属性 285 | setDrawerLayoutProperty(drawerLayout, contentLayout); 286 | addTranslucentView(activity, statusBarAlpha); 287 | } 288 | 289 | } 290 | 291 | /** 292 | * 设置 DrawerLayout 属性 293 | * 294 | * @param drawerLayout DrawerLayout 295 | * @param drawerLayoutContentLayout DrawerLayout 的内容布局 296 | */ 297 | private static void setDrawerLayoutProperty(DrawerLayout drawerLayout, ViewGroup drawerLayoutContentLayout) { 298 | ViewGroup drawer = (ViewGroup) drawerLayout.getChildAt(1); 299 | drawerLayout.setFitsSystemWindows(false); 300 | drawerLayoutContentLayout.setFitsSystemWindows(false); 301 | drawerLayoutContentLayout.setClipToPadding(true); 302 | drawer.setFitsSystemWindows(false); 303 | } 304 | 305 | /** 306 | * 为DrawerLayout 布局设置状态栏变色(5.0以下无半透明效果,不建议使用) 307 | * 308 | * @param activity 需要设置的activity 309 | * @param drawerLayout DrawerLayout 310 | * @param color 状态栏颜色值 311 | */ 312 | @Deprecated 313 | public static void setColorForDrawerLayoutDiff(Activity activity, DrawerLayout drawerLayout, @ColorInt int color) { 314 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 315 | activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); 316 | // 生成一个状态栏大小的矩形 317 | ViewGroup contentLayout = (ViewGroup) drawerLayout.getChildAt(0); 318 | View fakeStatusBarView = contentLayout.findViewById(FAKE_STATUS_BAR_VIEW_ID); 319 | if (fakeStatusBarView != null) { 320 | if (fakeStatusBarView.getVisibility() == View.GONE) { 321 | fakeStatusBarView.setVisibility(View.VISIBLE); 322 | } 323 | fakeStatusBarView.setBackgroundColor(calculateStatusColor(color, DEFAULT_STATUS_BAR_ALPHA)); 324 | } else { 325 | // 添加 statusBarView 到布局中 326 | contentLayout.addView(createStatusBarView(activity, color), 0); 327 | } 328 | // 内容布局不是 LinearLayout 时,设置padding top 329 | if (!(contentLayout instanceof LinearLayout) && contentLayout.getChildAt(1) != null) { 330 | contentLayout.getChildAt(1).setPadding(0, getStatusBarHeight(activity), 0, 0); 331 | } 332 | // 设置属性 333 | setDrawerLayoutProperty(drawerLayout, contentLayout); 334 | } 335 | } 336 | 337 | /** 338 | * 为 DrawerLayout 布局设置状态栏透明 339 | * 340 | * @param activity 需要设置的activity 341 | * @param drawerLayout DrawerLayout 342 | */ 343 | public static void setTranslucentForDrawerLayout(Activity activity, DrawerLayout drawerLayout) { 344 | setTranslucentForDrawerLayout(activity, drawerLayout, DEFAULT_STATUS_BAR_ALPHA); 345 | } 346 | 347 | /** 348 | * 为 DrawerLayout 布局设置状态栏透明 349 | * 350 | * @param activity 需要设置的activity 351 | * @param drawerLayout DrawerLayout 352 | */ 353 | public static void setTranslucentForDrawerLayout(Activity activity, DrawerLayout drawerLayout, int statusBarAlpha) { 354 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { 355 | return; 356 | } 357 | setTransparentForDrawerLayout(activity, drawerLayout); 358 | addTranslucentView(activity, statusBarAlpha); 359 | } 360 | 361 | /** 362 | * 为 DrawerLayout 布局设置状态栏透明 363 | * 364 | * @param activity 需要设置的activity 365 | * @param drawerLayout DrawerLayout 366 | */ 367 | public static void setTransparentForDrawerLayout(Activity activity, DrawerLayout drawerLayout) { 368 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { 369 | return; 370 | } 371 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 372 | activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); 373 | activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); 374 | activity.getWindow().setStatusBarColor(Color.TRANSPARENT); 375 | } else { 376 | activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); 377 | } 378 | 379 | ViewGroup contentLayout = (ViewGroup) drawerLayout.getChildAt(0); 380 | // 内容布局不是 LinearLayout 时,设置padding top 381 | if (!(contentLayout instanceof LinearLayout) && contentLayout.getChildAt(1) != null) { 382 | contentLayout.getChildAt(1).setPadding(0, getStatusBarHeight(activity), 0, 0); 383 | } 384 | 385 | // 设置属性 386 | setDrawerLayoutProperty(drawerLayout, contentLayout); 387 | } 388 | 389 | /** 390 | * 为 DrawerLayout 布局设置状态栏透明(5.0以上半透明效果,不建议使用) 391 | * 392 | * @param activity 需要设置的activity 393 | * @param drawerLayout DrawerLayout 394 | */ 395 | @Deprecated 396 | public static void setTranslucentForDrawerLayoutDiff(Activity activity, DrawerLayout drawerLayout) { 397 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 398 | // 设置状态栏透明 399 | activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); 400 | // 设置内容布局属性 401 | ViewGroup contentLayout = (ViewGroup) drawerLayout.getChildAt(0); 402 | contentLayout.setFitsSystemWindows(true); 403 | contentLayout.setClipToPadding(true); 404 | // 设置抽屉布局属性 405 | ViewGroup vg = (ViewGroup) drawerLayout.getChildAt(1); 406 | vg.setFitsSystemWindows(false); 407 | // 设置 DrawerLayout 属性 408 | drawerLayout.setFitsSystemWindows(false); 409 | } 410 | } 411 | 412 | /** 413 | * 为头部是 ImageView 的界面设置状态栏全透明 414 | * 415 | * @param activity 需要设置的activity 416 | * @param needOffsetView 需要向下偏移的 View 417 | */ 418 | public static void setTransparentForImageView(Activity activity, View needOffsetView) { 419 | setTranslucentForImageView(activity, 0, needOffsetView); 420 | } 421 | 422 | /** 423 | * 为头部是 ImageView 的界面设置状态栏透明(使用默认透明度) 424 | * 425 | * @param activity 需要设置的activity 426 | * @param needOffsetView 需要向下偏移的 View 427 | */ 428 | public static void setTranslucentForImageView(Activity activity, View needOffsetView) { 429 | setTranslucentForImageView(activity, DEFAULT_STATUS_BAR_ALPHA, needOffsetView); 430 | } 431 | 432 | /** 433 | * 为头部是 ImageView 的界面设置状态栏透明 434 | * 435 | * @param activity 需要设置的activity 436 | * @param statusBarAlpha 状态栏透明度 437 | * @param needOffsetView 需要向下偏移的 View 438 | */ 439 | public static void setTranslucentForImageView(Activity activity, int statusBarAlpha, View needOffsetView) { 440 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { 441 | return; 442 | } 443 | setTransparentForWindow(activity); 444 | addTranslucentView(activity, statusBarAlpha); 445 | if (needOffsetView != null) { 446 | ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) needOffsetView.getLayoutParams(); 447 | layoutParams.setMargins(layoutParams.leftMargin, layoutParams.topMargin + getStatusBarHeight(activity), 448 | layoutParams.rightMargin, layoutParams.bottomMargin); 449 | } 450 | } 451 | 452 | /** 453 | * 为 fragment 头部是 ImageView 的设置状态栏透明 454 | * 455 | * @param activity fragment 对应的 activity 456 | * @param needOffsetView 需要向下偏移的 View 457 | */ 458 | public static void setTranslucentForImageViewInFragment(Activity activity, View needOffsetView) { 459 | setTranslucentForImageViewInFragment(activity, DEFAULT_STATUS_BAR_ALPHA, needOffsetView); 460 | } 461 | 462 | /** 463 | * 为 fragment 头部是 ImageView 的设置状态栏透明 464 | * 465 | * @param activity fragment 对应的 activity 466 | * @param needOffsetView 需要向下偏移的 View 467 | */ 468 | public static void setTransparentForImageViewInFragment(Activity activity, View needOffsetView) { 469 | setTranslucentForImageViewInFragment(activity, 0, needOffsetView); 470 | } 471 | 472 | /** 473 | * 为 fragment 头部是 ImageView 的设置状态栏透明 474 | * 475 | * @param activity fragment 对应的 activity 476 | * @param statusBarAlpha 状态栏透明度 477 | * @param needOffsetView 需要向下偏移的 View 478 | */ 479 | public static void setTranslucentForImageViewInFragment(Activity activity, int statusBarAlpha, View needOffsetView) { 480 | setTranslucentForImageView(activity, statusBarAlpha, needOffsetView); 481 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { 482 | clearPreviousSetting(activity); 483 | } 484 | } 485 | 486 | /** 487 | * 隐藏伪状态栏 View 488 | * 489 | * @param activity 调用的 Activity 490 | */ 491 | public static void hideFakeStatusBarView(Activity activity) { 492 | ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView(); 493 | View fakeStatusBarView = decorView.findViewById(FAKE_STATUS_BAR_VIEW_ID); 494 | if (fakeStatusBarView != null) { 495 | fakeStatusBarView.setVisibility(View.GONE); 496 | } 497 | View fakeTranslucentView = decorView.findViewById(FAKE_TRANSLUCENT_VIEW_ID); 498 | if (fakeTranslucentView != null) { 499 | fakeTranslucentView.setVisibility(View.GONE); 500 | } 501 | } 502 | 503 | /////////////////////////////////////////////////////////////////////////////////// 504 | 505 | @TargetApi(Build.VERSION_CODES.KITKAT) 506 | private static void clearPreviousSetting(Activity activity) { 507 | ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView(); 508 | View fakeStatusBarView = decorView.findViewById(FAKE_STATUS_BAR_VIEW_ID); 509 | if (fakeStatusBarView != null) { 510 | decorView.removeView(fakeStatusBarView); 511 | ViewGroup rootView = (ViewGroup) ((ViewGroup) activity.findViewById(android.R.id.content)).getChildAt(0); 512 | rootView.setPadding(0, 0, 0, 0); 513 | } 514 | } 515 | 516 | /** 517 | * 添加半透明矩形条 518 | * 519 | * @param activity 需要设置的 activity 520 | * @param statusBarAlpha 透明值 521 | */ 522 | private static void addTranslucentView(Activity activity, int statusBarAlpha) { 523 | ViewGroup contentView = (ViewGroup) activity.findViewById(android.R.id.content); 524 | View fakeTranslucentView = contentView.findViewById(FAKE_TRANSLUCENT_VIEW_ID); 525 | if (fakeTranslucentView != null) { 526 | if (fakeTranslucentView.getVisibility() == View.GONE) { 527 | fakeTranslucentView.setVisibility(View.VISIBLE); 528 | } 529 | fakeTranslucentView.setBackgroundColor(Color.argb(statusBarAlpha, 0, 0, 0)); 530 | } else { 531 | contentView.addView(createTranslucentStatusBarView(activity, statusBarAlpha)); 532 | } 533 | } 534 | 535 | /** 536 | * 生成一个和状态栏大小相同的彩色矩形条 537 | * 538 | * @param activity 需要设置的 activity 539 | * @param color 状态栏颜色值 540 | * @return 状态栏矩形条 541 | */ 542 | private static StatusBarView createStatusBarView(Activity activity, @ColorInt int color) { 543 | return createStatusBarView(activity, color, 0); 544 | } 545 | 546 | /** 547 | * 生成一个和状态栏大小相同的半透明矩形条 548 | * 549 | * @param activity 需要设置的activity 550 | * @param color 状态栏颜色值 551 | * @param alpha 透明值 552 | * @return 状态栏矩形条 553 | */ 554 | private static StatusBarView createStatusBarView(Activity activity, @ColorInt int color, int alpha) { 555 | // 绘制一个和状态栏一样高的矩形 556 | StatusBarView statusBarView = new StatusBarView(activity); 557 | LinearLayout.LayoutParams params = 558 | new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(activity)); 559 | statusBarView.setLayoutParams(params); 560 | statusBarView.setBackgroundColor(calculateStatusColor(color, alpha)); 561 | statusBarView.setId(FAKE_STATUS_BAR_VIEW_ID); 562 | return statusBarView; 563 | } 564 | 565 | /** 566 | * 设置根布局参数 567 | */ 568 | private static void setRootView(Activity activity) { 569 | ViewGroup parent = (ViewGroup) activity.findViewById(android.R.id.content); 570 | for (int i = 0, count = parent.getChildCount(); i < count; i++) { 571 | View childView = parent.getChildAt(i); 572 | if (childView instanceof ViewGroup) { 573 | childView.setFitsSystemWindows(true); 574 | ((ViewGroup) childView).setClipToPadding(true); 575 | } 576 | } 577 | } 578 | 579 | /** 580 | * 设置透明 581 | */ 582 | private static void setTransparentForWindow(Activity activity) { 583 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 584 | activity.getWindow().setStatusBarColor(Color.TRANSPARENT); 585 | activity.getWindow() 586 | .getDecorView() 587 | .setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); 588 | } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 589 | activity.getWindow() 590 | .setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); 591 | } 592 | } 593 | 594 | /** 595 | * 使状态栏透明 596 | */ 597 | @TargetApi(Build.VERSION_CODES.KITKAT) 598 | private static void transparentStatusBar(Activity activity) { 599 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 600 | activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); 601 | activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); 602 | activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); 603 | activity.getWindow().setStatusBarColor(Color.TRANSPARENT); 604 | } else { 605 | activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); 606 | } 607 | } 608 | 609 | /** 610 | * 创建半透明矩形 View 611 | * 612 | * @param alpha 透明值 613 | * @return 半透明 View 614 | */ 615 | private static StatusBarView createTranslucentStatusBarView(Activity activity, int alpha) { 616 | // 绘制一个和状态栏一样高的矩形 617 | StatusBarView statusBarView = new StatusBarView(activity); 618 | LinearLayout.LayoutParams params = 619 | new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(activity)); 620 | statusBarView.setLayoutParams(params); 621 | statusBarView.setBackgroundColor(Color.argb(alpha, 0, 0, 0)); 622 | statusBarView.setId(FAKE_TRANSLUCENT_VIEW_ID); 623 | return statusBarView; 624 | } 625 | 626 | /** 627 | * 获取状态栏高度 628 | * 629 | * @param context context 630 | * @return 状态栏高度 631 | */ 632 | private static int getStatusBarHeight(Context context) { 633 | // 获得状态栏高度 634 | int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android"); 635 | return context.getResources().getDimensionPixelSize(resourceId); 636 | } 637 | 638 | /** 639 | * 计算状态栏颜色 640 | * 641 | * @param color color值 642 | * @param alpha alpha值 643 | * @return 最终的状态栏颜色 644 | */ 645 | private static int calculateStatusColor(@ColorInt int color, int alpha) { 646 | if (alpha == 0) { 647 | return color; 648 | } 649 | float a = 1 - alpha / 255f; 650 | int red = color >> 16 & 0xff; 651 | int green = color >> 8 & 0xff; 652 | int blue = color & 0xff; 653 | red = (int) (red * a + 0.5); 654 | green = (int) (green * a + 0.5); 655 | blue = (int) (blue * a + 0.5); 656 | return 0xff << 24 | red << 16 | green << 8 | blue; 657 | } 658 | 659 | } 660 | -------------------------------------------------------------------------------- /Timeline/app/src/main/java/com/lvr/timeline/utils/TUtil.java: -------------------------------------------------------------------------------- 1 | package com.lvr.timeline.utils; 2 | 3 | import java.lang.reflect.ParameterizedType; 4 | 5 | /** 6 | * 类转换初始化 7 | */ 8 | public class TUtil { 9 | public static T getT(Object o, int i) { 10 | try { 11 | return ((Class) ((ParameterizedType) (o.getClass() 12 | .getGenericSuperclass())).getActualTypeArguments()[i]) 13 | .newInstance(); 14 | } catch (InstantiationException e) { 15 | e.printStackTrace(); 16 | } catch (IllegalAccessException e) { 17 | e.printStackTrace(); 18 | } catch (ClassCastException e) { 19 | e.printStackTrace(); 20 | } 21 | return null; 22 | } 23 | 24 | public static Class forName(String className) { 25 | try { 26 | return Class.forName(className); 27 | } catch (ClassNotFoundException e) { 28 | e.printStackTrace(); 29 | } 30 | return null; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Timeline/app/src/main/java/com/lvr/timeline/widget/CircleImageView.java: -------------------------------------------------------------------------------- 1 | package com.lvr.timeline.widget; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.graphics.Bitmap; 6 | import android.graphics.BitmapShader; 7 | import android.graphics.Canvas; 8 | import android.graphics.Color; 9 | import android.graphics.Matrix; 10 | import android.graphics.Paint; 11 | import android.graphics.RectF; 12 | import android.graphics.Shader; 13 | import android.graphics.drawable.BitmapDrawable; 14 | import android.graphics.drawable.ColorDrawable; 15 | import android.graphics.drawable.Drawable; 16 | import android.util.AttributeSet; 17 | import android.widget.ImageView; 18 | 19 | import com.lvr.timeline.R; 20 | 21 | /** 22 | * Created by lvr on 2017/4/7. 23 | */ 24 | 25 | public class CircleImageView extends ImageView { 26 | /** 27 | * 圆形头像默认,CENTER_CROP!=系统默认的CENTER_CROP; 28 | * 将图片等比例缩放,让图像的长边边与ImageView的边长度相同,短边不够的留空白,缩放后截取圆形部分进行显示。 29 | */ 30 | private static final ScaleType SCALE_TYPE = ScaleType.CENTER_CROP; 31 | /** 32 | * 图片的压缩质量 33 | * ALPHA_8就是Alpha由8位组成,------ALPHA_8 代表8位Alpha位图 34 | * ARGB_4444就是由4个4位组成即16位,------ARGB_4444 代表16位ARGB位图 35 | * ARGB_8888就是由4个8位组成即32位,------ARGB_8888 代表32位ARGB位图 36 | * RGB_565就是R为5位,G为6位,B为5位共16位,------ARGB_565 代表8位RGB位图 37 | */ 38 | private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888; 39 | /** 40 | * 默认ColorDrawable的宽和高 41 | */ 42 | private static final int COLORDRAWABLE_DIMENSION = 1; 43 | /** 44 | * 默认边框宽度 45 | */ 46 | private static final int DEFAULT_BORDER_WIDTH = 0; 47 | /** 48 | * 默认边框颜色 49 | */ 50 | private static final int DEFAULT_BORDER_COLOR = Color.BLACK; 51 | /** 52 | * 画图片的矩形 53 | */ 54 | private final RectF mDrawableRect = new RectF(); 55 | /** 56 | * 画边框的矩形 57 | */ 58 | private final RectF mBorderRect = new RectF(); 59 | /** 60 | * 对图片进行缩放和移动的矩阵 61 | */ 62 | private final Matrix mShaderMatrix = new Matrix(); 63 | /** 64 | * 画图片的画笔 65 | */ 66 | private final Paint mBitmapPaint = new Paint(); 67 | /** 68 | * 画边框的画笔 69 | */ 70 | private final Paint mBorderPaint = new Paint(); 71 | /** 72 | * 默认边框颜色 73 | */ 74 | private int mBorderColor = DEFAULT_BORDER_COLOR; 75 | /** 76 | * 默认边框宽度 77 | */ 78 | private int mBorderWidth = DEFAULT_BORDER_WIDTH; 79 | 80 | private Bitmap mBitmap; 81 | /** 82 | * 产生一个画有一个位图的渲染器(Shader) 83 | */ 84 | private BitmapShader mBitmapShader; 85 | /** 86 | * 图片的实际宽度 87 | */ 88 | private int mBitmapWidth; 89 | /** 90 | * 图片实际高度 91 | */ 92 | private int mBitmapHeight; 93 | /** 94 | * 图片半径 95 | */ 96 | private float mDrawableRadius; 97 | /** 98 | * 边框半径 99 | */ 100 | private float mBorderRadius; 101 | /** 102 | * 是否初始化准备好 103 | */ 104 | private boolean mReady; 105 | /** 106 | * 内边距 107 | */ 108 | private boolean mSetupPending; 109 | 110 | public CircleImageView(Context context) { 111 | super(context); 112 | } 113 | 114 | public CircleImageView(Context context, AttributeSet attrs) { 115 | this(context, attrs, 0); 116 | } 117 | 118 | public CircleImageView(Context context, AttributeSet attrs, int defStyle) { 119 | super(context, attrs, defStyle); 120 | super.setScaleType(SCALE_TYPE); 121 | /** 122 | * 获取在xml中声明的属性 123 | */ 124 | TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView, defStyle, 0);//获取xml中的属性 125 | 126 | mBorderWidth = a.getDimensionPixelSize(R.styleable.CircleImageView_civ_border_width, DEFAULT_BORDER_WIDTH); 127 | mBorderColor = a.getColor(R.styleable.CircleImageView_civ_border_color, DEFAULT_BORDER_COLOR); 128 | 129 | a.recycle(); 130 | 131 | mReady = true; 132 | 133 | if (mSetupPending) { 134 | setup(); 135 | mSetupPending = false; 136 | } 137 | } 138 | 139 | @Override 140 | public ScaleType getScaleType() { 141 | return SCALE_TYPE; 142 | } 143 | 144 | @Override 145 | protected void onDraw(Canvas canvas) { 146 | if (getDrawable() == null) { 147 | return; 148 | } 149 | /** 150 | * 画圆形图片 151 | */ 152 | canvas.drawCircle(getWidth() / 2, getHeight() / 2, mDrawableRadius, mBitmapPaint); 153 | /** 154 | * 画圆形边框 155 | */ 156 | canvas.drawCircle(getWidth() / 2, getHeight() / 2, mBorderRadius, mBorderPaint); 157 | } 158 | 159 | @Override 160 | protected void onSizeChanged(int w, int h, int oldw, int oldh) { 161 | super.onSizeChanged(w, h, oldw, oldh); 162 | setup(); 163 | } 164 | 165 | /** 166 | * 获取边框颜色 167 | * 168 | * @return 169 | */ 170 | public int getBorderColor() { 171 | return mBorderColor; 172 | } 173 | 174 | /** 175 | * 设置边框颜色 176 | * 177 | * @param borderColor 178 | */ 179 | public void setBorderColor(int borderColor) { 180 | if (borderColor == mBorderColor) { 181 | return; 182 | } 183 | 184 | mBorderColor = borderColor; 185 | mBorderPaint.setColor(mBorderColor); 186 | invalidate(); 187 | } 188 | 189 | /** 190 | * 获取边框宽度 191 | * 192 | * @return 193 | */ 194 | public int getBorderWidth() { 195 | return mBorderWidth; 196 | } 197 | 198 | /** 199 | * 设置边框宽度 200 | * 201 | * @param borderWidth 202 | */ 203 | public void setBorderWidth(int borderWidth) { 204 | if (borderWidth == mBorderWidth) { 205 | return; 206 | } 207 | 208 | mBorderWidth = borderWidth; 209 | setup(); 210 | } 211 | 212 | /** 213 | * 设置资源图片 214 | * 215 | * @param bm 216 | */ 217 | @Override 218 | public void setImageBitmap(Bitmap bm) { 219 | super.setImageBitmap(bm); 220 | mBitmap = bm; 221 | setup(); 222 | } 223 | 224 | /** 225 | * 设置资源图片 226 | * 227 | * @param drawable 228 | */ 229 | @Override 230 | public void setImageDrawable(Drawable drawable) { 231 | super.setImageDrawable(drawable); 232 | mBitmap = getBitmapFromDrawable(drawable); 233 | setup(); 234 | } 235 | 236 | /** 237 | * 设置资源id 238 | * 239 | * @param resId 240 | */ 241 | @Override 242 | public void setImageResource(int resId) { 243 | super.setImageResource(resId); 244 | mBitmap = getBitmapFromDrawable(getDrawable()); 245 | setup(); 246 | } 247 | 248 | /** 249 | * 获取资源图片 250 | * 251 | * @param drawable 252 | * @return 253 | */ 254 | private Bitmap getBitmapFromDrawable(Drawable drawable) { 255 | if (drawable == null) { 256 | return null; 257 | } 258 | 259 | if (drawable instanceof BitmapDrawable) { 260 | return ((BitmapDrawable) drawable).getBitmap(); 261 | } 262 | 263 | try { 264 | Bitmap bitmap; 265 | 266 | if (drawable instanceof ColorDrawable) { 267 | bitmap = Bitmap.createBitmap(COLORDRAWABLE_DIMENSION, COLORDRAWABLE_DIMENSION, BITMAP_CONFIG); 268 | } else { 269 | bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), BITMAP_CONFIG); 270 | } 271 | 272 | Canvas canvas = new Canvas(bitmap); 273 | drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); 274 | drawable.draw(canvas); 275 | return bitmap; 276 | } catch (OutOfMemoryError e) { 277 | return null; 278 | } 279 | } 280 | 281 | /** 282 | * 画圆形图的方法 283 | */ 284 | private void setup() { 285 | if (!mReady) { 286 | mSetupPending = true; 287 | return; 288 | } 289 | 290 | if (mBitmap == null) { 291 | return; 292 | } 293 | /** 294 | *调用这个方法来产生一个画有一个位图的渲染器(Shader)。 295 | bitmap 在渲染器内使用的位图 296 | tileX The tiling mode for x to draw the bitmap in. 在位图上X方向花砖模式 297 | tileY The tiling mode for y to draw the bitmap in. 在位图上Y方向花砖模式 298 | TileMode:(一共有三种) 299 | CLAMP :如果渲染器超出原始边界范围,会复制范围内边缘染色。 300 | REPEAT :横向和纵向的重复渲染器图片,平铺。 301 | MIRROR :横向和纵向的重复渲染器图片,这个和REPEAT 重复方式不一样,他是以镜像方式平铺。 302 | */ 303 | mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); 304 | /** 305 | * 设置画圆形的画笔 306 | */ 307 | mBitmapPaint.setAntiAlias(true);//设置抗锯齿 308 | mBitmapPaint.setShader(mBitmapShader);//绘制图形时的图形变换 309 | 310 | mBorderPaint.setStyle(Paint.Style.STROKE); 311 | mBorderPaint.setAntiAlias(true); 312 | mBorderPaint.setColor(mBorderColor); 313 | mBorderPaint.setStrokeWidth(mBorderWidth); 314 | 315 | mBitmapHeight = mBitmap.getHeight(); 316 | mBitmapWidth = mBitmap.getWidth(); 317 | /** 318 | * 设置边框矩形的坐标 319 | */ 320 | mBorderRect.set(0, 0, getWidth(), getHeight()); 321 | /** 322 | * 设置边框圆形的半径为图片的宽度和高度的一半的最大值 323 | */ 324 | mBorderRadius = Math.max((mBorderRect.height() - mBorderWidth) / 2, (mBorderRect.width() - mBorderWidth) / 2); 325 | /** 326 | * 设置图片矩形的坐标 327 | */ 328 | mDrawableRect.set(mBorderWidth, mBorderWidth, mBorderRect.width() - mBorderWidth, mBorderRect.height() - mBorderWidth); 329 | /** 330 | * 设置图片圆形的半径为图片的宽度和高度的一半的最大值 331 | */ 332 | mDrawableRadius = Math.max(mDrawableRect.height() / 2, mDrawableRect.width() / 2); 333 | 334 | updateShaderMatrix(); 335 | /** 336 | * 调用onDraw()方法进行绘画 337 | */ 338 | invalidate(); 339 | } 340 | 341 | /** 342 | * 更新位图渲染 343 | */ 344 | private void updateShaderMatrix() { 345 | float scale; 346 | float dx = 0; 347 | float dy = 0; 348 | /** 349 | * 重置 350 | */ 351 | mShaderMatrix.set(null); 352 | /** 353 | *计算缩放比,因为如果图片的尺寸超过屏幕,那么就会自动匹配到屏幕的尺寸去显示。 354 | * 确定移动的xy坐标 355 | * 356 | */ 357 | if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width() * mBitmapHeight) { 358 | scale = mDrawableRect.width() / (float) mBitmapWidth; 359 | dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f; 360 | } else { 361 | scale = mDrawableRect.height() / (float) mBitmapHeight; 362 | dx = (mDrawableRect.width() - mBitmapWidth * scale) * 0.5f; 363 | } 364 | 365 | mShaderMatrix.setScale(scale, scale); 366 | mShaderMatrix.postTranslate((int) (dx + 0.5f) + mBorderWidth, (int) (dy + 0.5f) + mBorderWidth); 367 | /** 368 | * 设置shader的本地矩阵 369 | */ 370 | mBitmapShader.setLocalMatrix(mShaderMatrix); 371 | } 372 | } 373 | -------------------------------------------------------------------------------- /Timeline/app/src/main/java/com/lvr/timeline/widget/CircleTextView.java: -------------------------------------------------------------------------------- 1 | package com.lvr.timeline.widget; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.Color; 6 | import android.graphics.Paint; 7 | import android.util.AttributeSet; 8 | import android.widget.TextView; 9 | 10 | /** 11 | * Created by lvr on 2017/4/8. 12 | */ 13 | 14 | public class CircleTextView extends TextView { 15 | private Paint mBgPaint; 16 | 17 | public CircleTextView(Context context) { 18 | super(context); 19 | init(); 20 | } 21 | 22 | 23 | public CircleTextView(Context context, AttributeSet attrs, int defStyleAttr) { 24 | super(context, attrs, defStyleAttr); 25 | init(); 26 | } 27 | 28 | public CircleTextView(Context context, AttributeSet attrs) { 29 | super(context, attrs); 30 | init(); 31 | } 32 | 33 | private void init() { 34 | mBgPaint = new Paint(); 35 | mBgPaint.setAntiAlias(true); 36 | mBgPaint.setColor(Color.WHITE); 37 | } 38 | 39 | @Override 40 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 41 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 42 | int measuredWidth = getMeasuredWidth(); 43 | int measuredHeight = getMeasuredHeight(); 44 | int max = Math.max(measuredWidth, measuredHeight); 45 | setMeasuredDimension(max, max); 46 | } 47 | 48 | @Override 49 | public void setBackgroundColor(int color) { 50 | mBgPaint.setColor(color); 51 | } 52 | 53 | @Override 54 | protected void onDraw(Canvas canvas) { 55 | canvas.drawCircle(getWidth()/2,getHeight()/2,Math.max(getWidth(),getHeight())/2,mBgPaint); 56 | super.onDraw(canvas); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Timeline/app/src/main/java/com/lvr/timeline/widget/ItemDragHelperCallback.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 咖枯 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.lvr.timeline.widget; 18 | 19 | import android.support.v7.widget.GridLayoutManager; 20 | import android.support.v7.widget.LinearLayoutManager; 21 | import android.support.v7.widget.RecyclerView; 22 | import android.support.v7.widget.StaggeredGridLayoutManager; 23 | import android.support.v7.widget.helper.ItemTouchHelper; 24 | 25 | public class ItemDragHelperCallback extends ItemTouchHelper.Callback { 26 | private OnItemMoveListener mOnItemMoveListener; 27 | private OnItemSwipeListener mOnItemSwipeListener; 28 | 29 | public interface OnItemMoveListener { 30 | void onItemMove(int fromPosition, int toPosition); 31 | } 32 | public interface OnItemSwipeListener { 33 | void onItemSwipe(int position); 34 | } 35 | 36 | //相当于 set 设置监听 传入TimeAdapter中的OnItemMoveListener对象 37 | public ItemDragHelperCallback(OnItemMoveListener onItemMoveListener,OnItemSwipeListener onItemSwipeListener) { 38 | mOnItemMoveListener = onItemMoveListener; 39 | mOnItemSwipeListener = onItemSwipeListener; 40 | } 41 | //返回true 允许拖拽 42 | @Override 43 | public boolean isLongPressDragEnabled() { 44 | return true; 45 | } 46 | //返回true 允许滑动 47 | @Override 48 | public boolean isItemViewSwipeEnabled() { 49 | return true; 50 | } 51 | 52 | @Override 53 | public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { 54 | //根据recyclerView的布局,进行设置拖拽的方向 55 | int dragFlags = setDragFlags(recyclerView); 56 | //不允许进行滑动 57 | int swipeFlags = setSwipeFlags(recyclerView); 58 | return makeMovementFlags(dragFlags, swipeFlags); 59 | } 60 | 61 | private int setSwipeFlags(RecyclerView recyclerView) { 62 | int swipeFlags = 0; 63 | RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager(); 64 | if (layoutManager instanceof LinearLayoutManager ) { 65 | swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END; 66 | } 67 | return swipeFlags; 68 | } 69 | 70 | private int setDragFlags(RecyclerView recyclerView) { 71 | int dragFlags; 72 | RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager(); 73 | if (layoutManager instanceof GridLayoutManager || layoutManager instanceof StaggeredGridLayoutManager) { 74 | dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT; 75 | } else { 76 | dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN; 77 | } 78 | return dragFlags; 79 | } 80 | 81 | @Override 82 | public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { 83 | mOnItemMoveListener.onItemMove(viewHolder.getAdapterPosition(), target.getAdapterPosition()); 84 | return false; 85 | 86 | } 87 | 88 | 89 | @Override 90 | public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { 91 | super.clearView(recyclerView, viewHolder); 92 | 93 | } 94 | 95 | @Override 96 | public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { 97 | 98 | mOnItemSwipeListener.onItemSwipe(viewHolder.getAdapterPosition()); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Timeline/app/src/main/java/com/lvr/timeline/widget/StatusBarView.java: -------------------------------------------------------------------------------- 1 | package com.lvr.timeline.widget; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.view.View; 6 | 7 | /** 8 | * Created by Jaeger on 16/6/8. 9 | * 10 | * Email: chjie.jaeger@gmail.com 11 | * GitHub: https://github.com/laobie 12 | */ 13 | public class StatusBarView extends View { 14 | public StatusBarView(Context context, AttributeSet attrs) { 15 | super(context, attrs); 16 | } 17 | 18 | public StatusBarView(Context context) { 19 | super(context); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Timeline/app/src/main/res/drawable-xxhdpi/about.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRH1993/TimeLine/1c6adbba28633a2c0347538e704ef7bae8b273d1/Timeline/app/src/main/res/drawable-xxhdpi/about.png -------------------------------------------------------------------------------- /Timeline/app/src/main/res/drawable-xxhdpi/calender.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRH1993/TimeLine/1c6adbba28633a2c0347538e704ef7bae8b273d1/Timeline/app/src/main/res/drawable-xxhdpi/calender.png -------------------------------------------------------------------------------- /Timeline/app/src/main/res/drawable-xxhdpi/change.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRH1993/TimeLine/1c6adbba28633a2c0347538e704ef7bae8b273d1/Timeline/app/src/main/res/drawable-xxhdpi/change.png -------------------------------------------------------------------------------- /Timeline/app/src/main/res/drawable-xxhdpi/fb_plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRH1993/TimeLine/1c6adbba28633a2c0347538e704ef7bae8b273d1/Timeline/app/src/main/res/drawable-xxhdpi/fb_plus.png -------------------------------------------------------------------------------- /Timeline/app/src/main/res/drawable-xxhdpi/habbit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRH1993/TimeLine/1c6adbba28633a2c0347538e704ef7bae8b273d1/Timeline/app/src/main/res/drawable-xxhdpi/habbit.png -------------------------------------------------------------------------------- /Timeline/app/src/main/res/drawable-xxhdpi/ic_empty_picture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRH1993/TimeLine/1c6adbba28633a2c0347538e704ef7bae8b273d1/Timeline/app/src/main/res/drawable-xxhdpi/ic_empty_picture.png -------------------------------------------------------------------------------- /Timeline/app/src/main/res/drawable-xxhdpi/ic_image_loading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRH1993/TimeLine/1c6adbba28633a2c0347538e704ef7bae8b273d1/Timeline/app/src/main/res/drawable-xxhdpi/ic_image_loading.png -------------------------------------------------------------------------------- /Timeline/app/src/main/res/drawable-xxhdpi/ic_search_menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRH1993/TimeLine/1c6adbba28633a2c0347538e704ef7bae8b273d1/Timeline/app/src/main/res/drawable-xxhdpi/ic_search_menu.png -------------------------------------------------------------------------------- /Timeline/app/src/main/res/drawable-xxhdpi/item_delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRH1993/TimeLine/1c6adbba28633a2c0347538e704ef7bae8b273d1/Timeline/app/src/main/res/drawable-xxhdpi/item_delete.png -------------------------------------------------------------------------------- /Timeline/app/src/main/res/drawable-xxhdpi/mn_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRH1993/TimeLine/1c6adbba28633a2c0347538e704ef7bae8b273d1/Timeline/app/src/main/res/drawable-xxhdpi/mn_back.png -------------------------------------------------------------------------------- /Timeline/app/src/main/res/drawable-xxhdpi/mn_edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRH1993/TimeLine/1c6adbba28633a2c0347538e704ef7bae8b273d1/Timeline/app/src/main/res/drawable-xxhdpi/mn_edit.png -------------------------------------------------------------------------------- /Timeline/app/src/main/res/drawable-xxhdpi/mn_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRH1993/TimeLine/1c6adbba28633a2c0347538e704ef7bae8b273d1/Timeline/app/src/main/res/drawable-xxhdpi/mn_right.png -------------------------------------------------------------------------------- /Timeline/app/src/main/res/drawable-xxhdpi/moddy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRH1993/TimeLine/1c6adbba28633a2c0347538e704ef7bae8b273d1/Timeline/app/src/main/res/drawable-xxhdpi/moddy.png -------------------------------------------------------------------------------- /Timeline/app/src/main/res/drawable-xxhdpi/nav_header.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRH1993/TimeLine/1c6adbba28633a2c0347538e704ef7bae8b273d1/Timeline/app/src/main/res/drawable-xxhdpi/nav_header.jpg -------------------------------------------------------------------------------- /Timeline/app/src/main/res/drawable-xxhdpi/nav_photo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRH1993/TimeLine/1c6adbba28633a2c0347538e704ef7bae8b273d1/Timeline/app/src/main/res/drawable-xxhdpi/nav_photo.jpg -------------------------------------------------------------------------------- /Timeline/app/src/main/res/drawable-xxhdpi/out.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRH1993/TimeLine/1c6adbba28633a2c0347538e704ef7bae8b273d1/Timeline/app/src/main/res/drawable-xxhdpi/out.png -------------------------------------------------------------------------------- /Timeline/app/src/main/res/drawable-xxhdpi/progress.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRH1993/TimeLine/1c6adbba28633a2c0347538e704ef7bae8b273d1/Timeline/app/src/main/res/drawable-xxhdpi/progress.png -------------------------------------------------------------------------------- /Timeline/app/src/main/res/drawable-xxhdpi/select_time_online.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRH1993/TimeLine/1c6adbba28633a2c0347538e704ef7bae8b273d1/Timeline/app/src/main/res/drawable-xxhdpi/select_time_online.png -------------------------------------------------------------------------------- /Timeline/app/src/main/res/drawable-xxhdpi/select_today.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRH1993/TimeLine/1c6adbba28633a2c0347538e704ef7bae8b273d1/Timeline/app/src/main/res/drawable-xxhdpi/select_today.png -------------------------------------------------------------------------------- /Timeline/app/src/main/res/drawable-xxhdpi/select_yesterday.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRH1993/TimeLine/1c6adbba28633a2c0347538e704ef7bae8b273d1/Timeline/app/src/main/res/drawable-xxhdpi/select_yesterday.png -------------------------------------------------------------------------------- /Timeline/app/src/main/res/drawable-xxhdpi/sorry_hint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRH1993/TimeLine/1c6adbba28633a2c0347538e704ef7bae8b273d1/Timeline/app/src/main/res/drawable-xxhdpi/sorry_hint.png -------------------------------------------------------------------------------- /Timeline/app/src/main/res/drawable-xxhdpi/thing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRH1993/TimeLine/1c6adbba28633a2c0347538e704ef7bae8b273d1/Timeline/app/src/main/res/drawable-xxhdpi/thing.png -------------------------------------------------------------------------------- /Timeline/app/src/main/res/drawable-xxhdpi/time_online.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRH1993/TimeLine/1c6adbba28633a2c0347538e704ef7bae8b273d1/Timeline/app/src/main/res/drawable-xxhdpi/time_online.png -------------------------------------------------------------------------------- /Timeline/app/src/main/res/drawable-xxhdpi/today.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRH1993/TimeLine/1c6adbba28633a2c0347538e704ef7bae8b273d1/Timeline/app/src/main/res/drawable-xxhdpi/today.png -------------------------------------------------------------------------------- /Timeline/app/src/main/res/drawable-xxhdpi/toux2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRH1993/TimeLine/1c6adbba28633a2c0347538e704ef7bae8b273d1/Timeline/app/src/main/res/drawable-xxhdpi/toux2.png -------------------------------------------------------------------------------- /Timeline/app/src/main/res/drawable-xxhdpi/yesterday.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRH1993/TimeLine/1c6adbba28633a2c0347538e704ef7bae8b273d1/Timeline/app/src/main/res/drawable-xxhdpi/yesterday.png -------------------------------------------------------------------------------- /Timeline/app/src/main/res/drawable/select_button_countdown.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Timeline/app/src/main/res/drawable/select_button_today.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Timeline/app/src/main/res/drawable/select_button_yesterday.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Timeline/app/src/main/res/layout/activity_edit.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 15 | 21 | 22 | 31 | 32 | 33 | 39 | 40 | 41 | 50 | 51 | 52 | 57 | 58 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /Timeline/app/src/main/res/layout/activity_home.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 12 | 13 | 20 | 21 | 26 | 27 | 34 | 35 | 40 | 41 | 42 | 50 | 51 | 60 | 61 | 71 | 72 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 95 | 96 | -------------------------------------------------------------------------------- /Timeline/app/src/main/res/layout/app_bar.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Timeline/app/src/main/res/layout/dialog_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 17 | 18 | 27 | 30 |