Commit cc59ad65 by 胡学良

register page

parents
*.iml
.gradle
.idea
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
apply plugin: 'com.android.application'
android {
compileSdkVersion 29
buildToolsVersion "29.0.3"
defaultConfig {
applicationId "cn.yunliyunwai.beyondclouds"
minSdkVersion 19
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
javaCompileOptions {
annotationProcessorOptions {
includeCompileClasspath true
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility = 1.8
targetCompatibility = 1.8
}
viewBinding {
enabled true
}
dataBinding {
enabled true
}
}
dependencies {
def room_version = "2.2.3"
def retrofit_version = "2.6.4"
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
implementation 'com.google.android.material:material:1.1.0'
implementation 'com.github.hackware1993:MagicIndicator:1.5.0'
implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
implementation "com.squareup.retrofit2:converter-gson:$retrofit_version"
implementation 'org.jetbrains:annotations:19.0.0'
implementation 'org.projectlombok:lombok:1.18.12' //添加lombok依赖
implementation 'org.glassfish:javax.annotation:10.0-b28' //java注解
implementation 'com.google.dagger:dagger:2.26'
annotationProcessor 'com.google.dagger:dagger-compiler:2.26'
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"
implementation 'com.github.bumptech.glide:glide:4.11.0'
implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.50'
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
implementation 'com.facebook.fresco:fresco:0.12.0'
implementation 'com.facebook.fresco:animated-webp:0.12.0'
implementation 'com.facebook.fresco:webpsupport:0.12.0'
}
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
package cn.yunliyunwai.beyondclouds;
import android.content.Context;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
assertEquals("cn.yunliyunwai.beyondclouds", appContext.getPackageName());
}
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="cn.yunliyunwai.beyondclouds">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:name=".BeyondApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".ui.page.RegisterActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".ui.page.LoginActivity">
</activity>
<activity android:name=".ui.page.RegisterSuccess"></activity>
<activity
android:name=".SplashActivity"
android:theme="@style/AppTheme.FullScreen" />
<activity android:name=".MainActivity" />
</application>
</manifest>
\ No newline at end of file
package cn.yunliyunwai.beyondclouds;
import androidx.annotation.NonNull;
import androidx.lifecycle.ViewModelStore;
import androidx.lifecycle.ViewModelStoreOwner;
import cn.yunliyunwai.beyondclouds.di.component.AppComponent;
import cn.yunliyunwai.beyondclouds.di.component.DaggerAppComponent;
import cn.yunliyunwai.beyondclouds.util.TypeFaces;
public class BeyondApplication extends android.app.Application implements ViewModelStoreOwner {
AppComponent appComponent;
@Override
public void onCreate() {
super.onCreate();
TypeFaces.loadTypeFaces(this);
appComponent = DaggerAppComponent.factory().create(getApplicationContext());
}
private ViewModelStore mViewModelStore;
@NonNull
@Override
public ViewModelStore getViewModelStore() {
if (null == mViewModelStore) {
mViewModelStore = new ViewModelStore();
}
return mViewModelStore;
}
public AppComponent getAppComponent() {
return appComponent;
}
}
package cn.yunliyunwai.beyondclouds;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.MenuItem;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.ViewModelProvider;
import com.google.android.material.bottomnavigation.BottomNavigationView;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import cn.yunliyunwai.beyondclouds.base.BaseActivity;
import cn.yunliyunwai.beyondclouds.databinding.ActivityMainBinding;
import cn.yunliyunwai.beyondclouds.ui.DynamicFragment;
import cn.yunliyunwai.beyondclouds.ui.HomeFragment;
import cn.yunliyunwai.beyondclouds.ui.MyFragment;
import cn.yunliyunwai.beyondclouds.ui.ProjectFragment;
import cn.yunliyunwai.beyondclouds.ui.QAFragment;
import cn.yunliyunwai.beyondclouds.util.StatusBarUtils;
import cn.yunliyunwai.beyondclouds.viewmodel.MainActivityViewModel;
public class MainActivity extends BaseActivity<MainActivityViewModel, ActivityMainBinding> implements BottomNavigationView.OnNavigationItemSelectedListener {
private static final String TAG = MainActivity.class.getCanonicalName();
private List<Fragment> fragments;
@Inject
ViewModelProvider.Factory factory;
@Override
protected void onCreate(Bundle savedInstanceState) {
StatusBarUtils.setWindowStatusBarColor(this, R.color.colorStatusBar);
super.onCreate(savedInstanceState);
initBottomNavigation(savedInstanceState);
}
@Override
protected void initInject() {
((BeyondApplication) getApplication()).getAppComponent().inject(this);
}
@Override
protected MainActivityViewModel initViewModel() {
return new ViewModelProvider(this, factory).get(MainActivityViewModel.class);
}
@Override
protected ActivityMainBinding initDataBinding(LayoutInflater inflater) {
return ActivityMainBinding.inflate(inflater);
}
private void initBottomNavigation(Bundle savedInstanceState) {
fragments = new ArrayList<>(5);
fragments.add(new HomeFragment());
fragments.add(new ProjectFragment());
fragments.add(new DynamicFragment());
fragments.add(new QAFragment());
fragments.add(new MyFragment());
if (null != savedInstanceState) {
for (Fragment fragment : getSupportFragmentManager().getFragments()) {
fragments.set(Integer.valueOf(fragment.getTag()), fragment);
}
switchFragment(binding.getViewModel().getCurrentIndex());
} else {
switchFragment(0);
}
binding.bottomNavigationView.setOnNavigationItemSelectedListener(this);
binding.bottomNavigationView.setItemIconTintList(null);
}
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.home_fragment:
switchFragment(0);
break;
case R.id.project_fragment:
switchFragment(1);
break;
case R.id.dynamic_fragment:
switchFragment(2);
break;
case R.id.qa_fragment:
switchFragment(3);
break;
case R.id.my_fragment:
switchFragment(4);
break;
}
return true;
}
private void switchFragment(int index) {
int previousIndex = binding.getViewModel().getCurrentIndex();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
if (-1 != previousIndex) {
transaction.hide(fragments.get(previousIndex));
}
if (fragments.get(index).isAdded()) {
transaction.show(fragments.get(index));
} else {
transaction.add(R.id.fragment_container, fragments.get(index), String.valueOf(index));
}
transaction.commit();
binding.getViewModel().switchFrame(index);
}
}
package cn.yunliyunwai.beyondclouds;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import androidx.annotation.Nullable;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import javax.inject.Inject;
import cn.yunliyunwai.beyondclouds.base.BaseActivity;
import cn.yunliyunwai.beyondclouds.databinding.ActivitySplashBinding;
import cn.yunliyunwai.beyondclouds.util.ContextUtils;
import cn.yunliyunwai.beyondclouds.viewmodel.SplashActivityViewModel;
/**
* 欢迎屏幕
*/
public class SplashActivity extends BaseActivity<SplashActivityViewModel, ActivitySplashBinding> {
@Inject
ViewModelProvider.Factory factory;
@Override
protected void initInject() {
ContextUtils.getAppComponent(this).splashActivityComponent().create().inject(this);
}
@Override
protected SplashActivityViewModel initViewModel() {
return new ViewModelProvider(this, factory).get(SplashActivityViewModel.class);
}
@Override
protected ActivitySplashBinding initDataBinding(LayoutInflater inflater) {
return ActivitySplashBinding.inflate(inflater);
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
viewModel.getInitialStates().observe(this, new Observer<SplashActivityViewModel.InitialStates>() {
@Override
public void onChanged(SplashActivityViewModel.InitialStates initialStates) {
if (initialStates.isCompleted()) {
startActivity(new Intent(SplashActivity.this, MainActivity.class));
SplashActivity.this.finish();
}
}
});
binding.getViewModel().initialize();
}
}
package cn.yunliyunwai.beyondclouds.adapter;
import android.text.TextUtils;
import android.view.View;
import com.bumptech.glide.Glide;
import com.bumptech.glide.Priority;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.request.RequestOptions;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.BaseViewHolder;
import cn.yunliyunwai.beyondclouds.R;
import cn.yunliyunwai.beyondclouds.data.model.Blog;
import cn.yunliyunwai.beyondclouds.databinding.BlogItemBinding;
import cn.yunliyunwai.beyondclouds.util.DateUtils;
import cn.yunliyunwai.beyondclouds.util.GlideRoundTransform;
public class BlogListRecyclerViewAdapter extends BaseQuickAdapter<Blog ,BaseViewHolder> {
private static final GlideRoundTransform glideRoundTransform = new GlideRoundTransform(3);
public BlogListRecyclerViewAdapter(int layoutResId) {
super(layoutResId);
}
@Override
protected void convert(BaseViewHolder helper, Blog blog) {
BlogItemBinding binding = BlogItemBinding.bind(helper.itemView);
binding.txtTitle.setText(blog.getBlogTitle());
binding.txtAbstract.setText(blog.getBlogAbstract());
binding.txtAuthor.setText(blog.getUserNick());
binding.txtCreateTime.setText(DateUtils.format(blog.getCreateTime()));
if (null != blog.getCover() && !TextUtils.isEmpty(blog.getCover().trim()) && blog.getCover().startsWith("http")) {
binding.blogCover.setVisibility(View.VISIBLE);
RequestOptions options = new RequestOptions()
.centerCrop()
.placeholder(R.drawable.placeholder_rectangle)
.error(R.drawable.placeholder_rectangle)
.priority(Priority.HIGH)
.skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.transform(glideRoundTransform);
Glide.with(binding.getRoot()).applyDefaultRequestOptions(options).load(blog.getCover()).into(binding.blogCover);
} else {
binding.blogCover.setVisibility(View.GONE);
}
}
}
\ No newline at end of file
package cn.yunliyunwai.beyondclouds.adapter;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.TextPaint;
import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
import android.text.style.ClickableSpan;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import com.bumptech.glide.Glide;
import com.bumptech.glide.Priority;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.request.RequestOptions;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.BaseViewHolder;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import cn.yunliyunwai.beyondclouds.R;
import cn.yunliyunwai.beyondclouds.data.model.Post;
import cn.yunliyunwai.beyondclouds.databinding.PostItemBinding;
import cn.yunliyunwai.beyondclouds.util.DateUtils;
import cn.yunliyunwai.beyondclouds.util.GlideRoundTransform;
import lombok.Data;
public class DynamicListRecyclerViewAdapter extends BaseQuickAdapter<Post,BaseViewHolder> {
@Data
static class PostLink {
enum LinkType {
TOPIC,
OTHER
}
private int start;
private int end;
private String content;
private String link;
private LinkType linkType;
public PostLink(int start, int end, String content, String link, LinkType linkType) {
this.start = start;
this.end = end;
this.content = content;
this.link = link;
this.linkType = linkType;
}
public static PostLink of(int start, int end, String content, String link, LinkType linkType) {
return new PostLink(start, end, content, link, linkType);
}
}
private static final Pattern TOPIC_LINK_PATTERN = Pattern.compile("<a.+?href=['\"](.+?)['\"]>(.+?)</a>");
private static final String TOPIC_BARRIER = "#";
private static final GlideRoundTransform glideRoundTransform = new GlideRoundTransform(20);
public DynamicListRecyclerViewAdapter(int layoutResId) {
super(layoutResId);
}
@Override
protected void convert(BaseViewHolder helper, Post post) {
PostItemBinding binding = PostItemBinding.bind(helper.itemView);
parseAndSetContent(binding.txtContent, post.getContent());
binding.txtUserNick.setText(post.getUserNick());
binding.txtCreateTime.setText(DateUtils.format(post.getCreateTime()));
if (null != post.getUserAvatar() && !TextUtils.isEmpty(post.getUserAvatar().trim()) && post.getUserAvatar().startsWith("http")) {
RequestOptions options = new RequestOptions()
.centerCrop()
.placeholder(R.drawable.placeholder_circle)
.error(R.drawable.placeholder_circle)
.priority(Priority.HIGH)
.skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.transform(glideRoundTransform);
Glide.with(binding.getRoot()).applyDefaultRequestOptions(options).load(post.getUserAvatar()).into(binding.imgUserAvatar);
}
}
static class TopicLinkClickable extends ClickableSpan {
@Override
public void onClick(@NonNull View widget) {
Toast.makeText(widget.getContext(), "点击了链接", Toast.LENGTH_SHORT).show();
}
@Override
public void updateDrawState(@NonNull TextPaint ds) {
ds.setUnderlineText(false);
ds.setColor(ds.linkColor);
}
}
private void parseAndSetContent(TextView textView, String content) {
content = content.replace("<br>", "\n");
content = content.replace("<br/>", "\n");
SpannableStringBuilder builder = new SpannableStringBuilder();
List<PostLink> postLinkList = new ArrayList<>(10);
Matcher matcher = TOPIC_LINK_PATTERN.matcher(content);
while (matcher.find()) {
String link = matcher.group(1);
String linkContent = matcher.group(2);
PostLink.LinkType linkType =
(linkContent.startsWith(TOPIC_BARRIER) && linkContent.endsWith(TOPIC_BARRIER))
? PostLink.LinkType.TOPIC
: PostLink.LinkType.OTHER;
postLinkList.add(PostLink.of(matcher.start(), matcher.end(), linkContent, link, linkType));
}
int currentIndex = 0;
for (PostLink postLink : postLinkList) {
String plainText = content.substring(currentIndex, postLink.start);
if (!TextUtils.isEmpty(plainText)) {
builder.append(plainText);
}
SpannableString topicSpannable = new SpannableString(postLink.content);
topicSpannable.setSpan(new TopicLinkClickable(),0, topicSpannable.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
builder.append(topicSpannable);
currentIndex = postLink.end;
}
if (currentIndex != content.length()) {
builder.append(content.substring(currentIndex));
}
textView.setText(builder);
textView.setMovementMethod(LinkMovementMethod.getInstance());
}
}
\ No newline at end of file
package cn.yunliyunwai.beyondclouds.adapter;
import android.text.TextUtils;
import android.view.View;
import com.bumptech.glide.Glide;
import com.bumptech.glide.Priority;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.request.RequestOptions;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.BaseViewHolder;
import cn.yunliyunwai.beyondclouds.R;
import cn.yunliyunwai.beyondclouds.data.model.Project;
import cn.yunliyunwai.beyondclouds.databinding.ProjectItemBinding;
import cn.yunliyunwai.beyondclouds.util.DateUtils;
import cn.yunliyunwai.beyondclouds.util.GlideRoundTransform;
public class ProjectListRecyclerViewAdapter extends BaseQuickAdapter<Project,BaseViewHolder> {
private static final GlideRoundTransform glideRoundTransform = new GlideRoundTransform(3);
public ProjectListRecyclerViewAdapter(int layoutResId) {
super(layoutResId);
}
@Override
protected void convert(BaseViewHolder helper, Project project) {
ProjectItemBinding binding = ProjectItemBinding.bind(helper.itemView);
binding.txtTitle.setText(project.getProjectName());
binding.txtAbstract.setText(project.getProjectName());
binding.txtAuthor.setText(project.getUserNick());
binding.txtCreateTime.setText(DateUtils.format(project.getCreateTime()));
if (null != project.getCover() && !TextUtils.isEmpty(project.getCover().trim()) && project.getCover().startsWith("http")) {
binding.blogCover.setVisibility(View.VISIBLE);
RequestOptions options = new RequestOptions()
.centerCrop()
.placeholder(R.drawable.placeholder_rectangle)
.error(R.drawable.placeholder_rectangle)
.priority(Priority.HIGH)
.skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.transform(glideRoundTransform);
Glide.with(binding.getRoot()).applyDefaultRequestOptions(options).load(project.getCover()).into(binding.blogCover);
} else {
binding.blogCover.setVisibility(View.GONE);
}
}
}
\ No newline at end of file
package cn.yunliyunwai.beyondclouds.adapter;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.BaseViewHolder;
import cn.yunliyunwai.beyondclouds.data.model.Question;
import cn.yunliyunwai.beyondclouds.databinding.QuestionItemBinding;
import cn.yunliyunwai.beyondclouds.util.DateUtils;
import cn.yunliyunwai.beyondclouds.util.GlideRoundTransform;
public class QuestionListRecyclerViewAdapter extends BaseQuickAdapter<Question,BaseViewHolder> {
private static final GlideRoundTransform glideRoundTransform = new GlideRoundTransform(3);
public QuestionListRecyclerViewAdapter(int layoutResId) {
super(layoutResId);
}
@Override
protected void convert(BaseViewHolder helper, Question question) {
QuestionItemBinding binding = QuestionItemBinding.bind(helper.itemView);
binding.txtTitle.setText(question.getQuestionTitle());
binding.txtAbstract.setText(question.getQuestionAbstract());
binding.txtAuthor.setText(question.getUserNick());
binding.txtCreateTime.setText(DateUtils.format(question.getCreateTime()));
}
}
\ No newline at end of file
package cn.yunliyunwai.beyondclouds.adapter.common;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentStatePagerAdapter;
import java.util.List;
import cn.yunliyunwai.beyondclouds.data.model.CommonCategory;
import cn.yunliyunwai.beyondclouds.ui.common.FragmentFactory;
public class CategoryFragmentPagerAdapter extends FragmentStatePagerAdapter {
private List<CommonCategory> categories;
private FragmentFactory factory;
public CategoryFragmentPagerAdapter(@NonNull FragmentManager fm, List<CommonCategory> categories, FragmentFactory factory) {
super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
this.categories = categories;
this.factory = factory;
}
@NonNull
@Override
public Fragment getItem(int position) {
Fragment fragment = factory.create();
Bundle bundle = new Bundle();
bundle.putParcelable("category", categories.get(position));
fragment.setArguments(bundle);
return fragment;
}
@Override
public int getCount() {
return categories.size();
}
}
package cn.yunliyunwai.beyondclouds.adapter.common;
import android.content.Context;
import android.graphics.Typeface;
import androidx.viewpager.widget.ViewPager;
import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.CommonNavigatorAdapter;
import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IPagerIndicator;
import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IPagerTitleView;
import java.util.List;
import cn.yunliyunwai.beyondclouds.R;
import cn.yunliyunwai.beyondclouds.data.model.CommonCategory;
import cn.yunliyunwai.beyondclouds.view.CommonPagerIndicator;
import cn.yunliyunwai.beyondclouds.view.ScaleTransitionPagerTitleView;
public class CategoryNavigatorAdapter extends CommonNavigatorAdapter {
private List<CommonCategory> categories;
private ViewPager viewPager;
public CategoryNavigatorAdapter(List<CommonCategory> categories, ViewPager viewPager) {
this.categories = categories;
this.viewPager = viewPager;
}
@Override
public int getCount() {
return categories.size();
}
@Override
public IPagerTitleView getTitleView(Context context, int index) {
ScaleTransitionPagerTitleView scaleTransitionPagerTitleView = ScaleTransitionPagerTitleView.createDefaultTitleView(context, index);
scaleTransitionPagerTitleView.setText(categories.get(index).getCategoryName());
scaleTransitionPagerTitleView.setTypeface(Typeface.DEFAULT);
scaleTransitionPagerTitleView.setOnClickListener(view -> viewPager.setCurrentItem(index));
return scaleTransitionPagerTitleView;
}
@Override
public IPagerIndicator getIndicator(Context context) {
CommonPagerIndicator indicator = new CommonPagerIndicator(context);
float density = context.getResources().getDisplayMetrics().density;
indicator.setIndicatorDrawable(context.getResources().getDrawable(R.drawable.tab_indicator));
indicator.setMode(CommonPagerIndicator.MODE_EXACTLY);
indicator.setDrawableHeight(2 * density);
indicator.setDrawableWidth(8 * density);
return indicator;
}
}
package cn.yunliyunwai.beyondclouds.adapter.common;
import androidx.lifecycle.LiveData;
import java.lang.reflect.Type;
import java.util.concurrent.atomic.AtomicBoolean;
import cn.yunliyunwai.beyondclouds.data.model.Result;
import retrofit2.Call;
import retrofit2.CallAdapter;
import retrofit2.Callback;
import retrofit2.Response;
public class LiveDataCallAdapter<T> implements CallAdapter<T, LiveData<T>> {
private Type responseType;
public LiveDataCallAdapter(Type responseType) {
this.responseType = responseType;
}
public Type responseType() {
return responseType;
}
@Override
public LiveData<T> adapt(Call<T> call) {
return new LiveData<T>() {
private AtomicBoolean started = new AtomicBoolean(false);
@Override
protected void onActive() {
super.onActive();
if (started.compareAndSet(false, true)) {
call.enqueue(new Callback<T>() {
@Override
public void onResponse(Call<T> call, Response<T> response) {
if(response.code() == 200) {
postValue(response.body());
} else {
postValue((T) new Result(-10, "请求出错", response.errorBody()));
}
}
@Override
public void onFailure(Call<T> call, Throwable t) {
T value = (T) new Result(-10, "请求出错", "");
postValue(value);
}
});
}
}
};
}
}
package cn.yunliyunwai.beyondclouds.adapter.common;
import androidx.lifecycle.LiveData;
import org.jetbrains.annotations.Nullable;
import java.lang.annotation.Annotation;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import cn.yunliyunwai.beyondclouds.data.model.Result;
import retrofit2.CallAdapter;
import retrofit2.Retrofit;
public class LiveDataCallAdapterFactory extends CallAdapter.Factory {
@Nullable
@Override
public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
if (getRawType(returnType) != LiveData.class) {
return null;
}
//获取第一个泛型类型
Type observableType = getParameterUpperBound(0, (ParameterizedType) returnType);
Class rawType = getRawType(observableType);
if (rawType != Result.class) {
throw new IllegalArgumentException("type must be ApiResponse");
}
if (!(observableType instanceof ParameterizedType)) {
throw new IllegalArgumentException("resource must be parameterized");
}
return new LiveDataCallAdapter<>(observableType);
}
}
package cn.yunliyunwai.beyondclouds.base;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.ViewDataBinding;
import androidx.lifecycle.ViewModel;
import androidx.viewbinding.ViewBinding;
import cn.yunliyunwai.beyondclouds.util.BindingUtils;
public abstract class BaseActivity<VM extends ViewModel, B extends ViewBinding> extends AppCompatActivity {
private static final String TAG = BaseActivity.class.getCanonicalName();
protected B binding;
protected VM viewModel;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initInject();
viewModel = initViewModel();
binding = initDataBinding(getLayoutInflater());
if (binding instanceof ViewDataBinding) {
bindViewModel(binding, viewModel);
}
setContentView(binding.getRoot());
}
protected abstract void initInject();
protected void bindViewModel(@NonNull B binding, @NonNull ViewModel viewModel) {
boolean bindSuccess = BindingUtils.bindViewModel(binding, viewModel);
if (!bindSuccess) {
Log.w(TAG, "viewModel 绑定失败,请重写bindViewModel方法自定义绑定");
}
}
/**
* 初始化viewModel
* @return
*/
protected abstract VM initViewModel();
/**
* 初始化dataBinding
* @return
*/
protected abstract B initDataBinding(LayoutInflater inflater);
}
package cn.yunliyunwai.beyondclouds.base;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.ViewDataBinding;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModel;
import androidx.viewbinding.ViewBinding;
import cn.yunliyunwai.beyondclouds.util.BindingUtils;
public abstract class BaseFragment<VM extends ViewModel, B extends ViewBinding> extends Fragment {
private static final String TAG = BaseFragment.class.getCanonicalName();
protected B binding;
protected VM viewModel;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
initInject();
viewModel = initViewModel();
binding = initDataBinding(inflater, container);
if (binding instanceof ViewDataBinding) {
bindViewModel(binding, viewModel);
}
return binding.getRoot();
}
protected abstract void initInject();
protected abstract VM initViewModel();
/**
* 初始化dataBinding
* @return
*/
protected abstract B initDataBinding(@NonNull LayoutInflater inflater, @Nullable ViewGroup container);
private void bindViewModel(B binding, VM viewModel) {
boolean bindSuccess = BindingUtils.bindViewModel(binding, viewModel);
if (!bindSuccess) {
Log.w(TAG, "viewModel 绑定失败,请重写bindViewModel方法自定义绑定");
}
}
}
package cn.yunliyunwai.beyondclouds.base;
import androidx.lifecycle.ViewModel;
public abstract class BaseViewModel extends ViewModel {
}
package cn.yunliyunwai.beyondclouds.data.converters;
import androidx.room.TypeConverter;
import java.util.Date;
public class DateConverters {
@TypeConverter
public static Date fromTimestamp(Long value) {
return value == null ? null : new Date(value);
}
@TypeConverter
public static Long dateToTimestamp(Date date) {
return date == null ? null : date.getTime();
}
}
package cn.yunliyunwai.beyondclouds.data.model;
import java.util.Date;
import lombok.Data;
@Data
public class Blog {
private String blogId;
private String userId;
private String categoryId;
private String category;
private String blogTitle;
private String blogAbstract;
private String cover;
private String originLink;
private String viewPrivileges;
private String allowComment;
private String allowForward;
private String userNick;
private Date createTime;
}
package cn.yunliyunwai.beyondclouds.data.model;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
import androidx.room.TypeConverters;
import java.util.Date;
import cn.yunliyunwai.beyondclouds.data.converters.DateConverters;
@Entity(tableName = "BlogCategory")
public class BlogCategory {
@PrimaryKey
@ColumnInfo(name = "category_id")
private Integer categoryId;
@ColumnInfo(name = "category")
private String category;
@ColumnInfo(name = "priority")
private Integer priority;
@TypeConverters(DateConverters.class)
@ColumnInfo(name = "create_time")
private Date createTime;
public Integer getCategoryId() {
return categoryId;
}
public void setCategoryId(Integer categoryId) {
this.categoryId = categoryId;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public Integer getPriority() {
return priority;
}
public void setPriority(Integer priority) {
this.priority = priority;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
}
package cn.yunliyunwai.beyondclouds.data.model;
import android.os.Parcel;
import android.os.Parcelable;
import lombok.Data;
@Data
public class CommonCategory implements Parcelable {
private static final String CATEGORY_NAME_RECOMMEND = "推荐";
private static final String CATEGORY_NAME_LATEST = "最新";
private static final String CATEGORY_NAME_FOLLOW = "关注";
private static final String CATEGORY_NAME_MY = "我的";
private static final String CATEGORY_NAME_WORDS = "文字";
private static final String CATEGORY_NAME_VIDEOS = "视频";
private static final String CATEGORY_NAME_PHOTOS = "图片";
private static final String CATEGORY_NAME_HOTS = "热门";
protected CommonCategory(Parcel in) {
if (in.readByte() == 0) {
categoryId = null;
} else {
categoryId = in.readInt();
}
categoryName = in.readString();
categoryType = in.readParcelable(CategoryType.class.getClassLoader());
}
public static final Creator<CommonCategory> CREATOR = new Creator<CommonCategory>() {
@Override
public CommonCategory createFromParcel(Parcel in) {
return new CommonCategory(in);
}
@Override
public CommonCategory[] newArray(int size) {
return new CommonCategory[size];
}
};
public CommonCategory(Integer categoryId, String categoryName, CategoryType categoryType) {
this.categoryId = categoryId;
this.categoryName = categoryName;
this.categoryType = categoryType;
}
private Integer categoryId;
private String categoryName;
private CategoryType categoryType;
/**
* 推荐
* @return
*/
public static CommonCategory recommend() {
return new CommonCategory(null, CATEGORY_NAME_RECOMMEND, CategoryType.RECOMMEND);
}
/**
* 最新
* @return
*/
public static CommonCategory latest() {
return new CommonCategory(null, CATEGORY_NAME_LATEST, CategoryType.LATEST);
}
/**
* 关注
* @return
*/
public static CommonCategory follow() {
return new CommonCategory(null, CATEGORY_NAME_FOLLOW, CategoryType.FOLLOW);
}
/**
* 我的
* @return
*/
public static CommonCategory my() {
return new CommonCategory(null, CATEGORY_NAME_MY, CategoryType.MY);
}
/**
* 视频
* @return
*/
public static CommonCategory videos() {
return new CommonCategory(null, CATEGORY_NAME_VIDEOS, CategoryType.VIDEOS);
}
/**
* 文字
* @return
*/
public static CommonCategory words() {
return new CommonCategory(null, CATEGORY_NAME_WORDS, CategoryType.WORDS);
}
/**
* 图片
* @return
*/
public static CommonCategory photos() {
return new CommonCategory(null, CATEGORY_NAME_PHOTOS, CategoryType.PHOTOS);
}
/**
* 热门
* @return
*/
public static CommonCategory hots() {
return new CommonCategory(null, CATEGORY_NAME_HOTS, CategoryType.HOTS);
}
/**
* 实际栏目
* @param categoryId
* @param categoryName
* @return
*/
public static CommonCategory real(Integer categoryId, String categoryName) {
return new CommonCategory(categoryId, categoryName, CategoryType.REAL);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(categoryId);
dest.writeParcelable(categoryType, 0);
dest.writeString(categoryName);
}
public enum CategoryType implements Parcelable {
RECOMMEND,
HOTS,
LATEST,
FOLLOW,
MY,
VIDEOS,
WORDS,
PHOTOS,
REAL;
public static final Creator<CategoryType> CREATOR = new Creator<CategoryType>() {
@Override
public CategoryType createFromParcel(Parcel in) {
return CategoryType.values()[in.readInt()];
}
@Override
public CategoryType[] newArray(int size) {
return new CategoryType[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
}
}
}
package cn.yunliyunwai.beyondclouds.data.model;
import java.util.List;
public class Page<T> {
private Integer totalPage;
private Integer totalRecords;
private List<T> dataList;
public Integer getTotalPage() {
return totalPage;
}
public void setTotalPage(Integer totalPage) {
this.totalPage = totalPage;
}
public Integer getTotalRecords() {
return totalRecords;
}
public void setTotalRecords(Integer totalRecords) {
this.totalRecords = totalRecords;
}
public List<T> getDataList() {
return dataList;
}
public void setDataList(List<T> dataList) {
this.dataList = dataList;
}
}
package cn.yunliyunwai.beyondclouds.data.model;
import java.util.Date;
import java.util.List;
import lombok.Data;
@Data
public class Post {
private String postId;
private String userNick;
private String userAvatar;
private String video;
private List<String> pictures;
private String content;
private Date createTime;
}
package cn.yunliyunwai.beyondclouds.data.model;
import java.util.Date;
import lombok.Data;
@Data
public class Project {
private String projectId;
private String userId;
private String categoryId;
private String category;
private String cover;
private String projectName;
private String userNick;
private Date createTime;
}
package cn.yunliyunwai.beyondclouds.data.model;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
import androidx.room.TypeConverters;
import java.util.Date;
import cn.yunliyunwai.beyondclouds.data.converters.DateConverters;
@Entity(tableName = "ProjectCategory")
public class ProjectCategory {
@PrimaryKey
@ColumnInfo(name = "category_id")
private Integer categoryId;
@ColumnInfo(name = "category")
private String category;
@ColumnInfo(name = "priority")
private Integer priority;
@TypeConverters(DateConverters.class)
@ColumnInfo(name = "create_time")
private Date createTime;
public Integer getCategoryId() {
return categoryId;
}
public void setCategoryId(Integer categoryId) {
this.categoryId = categoryId;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public Integer getPriority() {
return priority;
}
public void setPriority(Integer priority) {
this.priority = priority;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
}
package cn.yunliyunwai.beyondclouds.data.model;
import java.util.Date;
import lombok.Data;
@Data
public class Question {
private String questionId;
private String questionTitle;
private String questionAbstract;
private String userNick;
private Date createTime;
}
package cn.yunliyunwai.beyondclouds.data.model;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
import androidx.room.TypeConverters;
import java.util.Date;
import cn.yunliyunwai.beyondclouds.data.converters.DateConverters;
@Entity(tableName = "QuestionCategory")
public class QuestionCategory {
@PrimaryKey
@ColumnInfo(name = "category_id")
private Integer categoryId;
@ColumnInfo(name = "category")
private String category;
@ColumnInfo(name = "priority")
private Integer priority;
@TypeConverters(DateConverters.class)
@ColumnInfo(name = "create_time")
private Date createTime;
public Integer getCategoryId() {
return categoryId;
}
public void setCategoryId(Integer categoryId) {
this.categoryId = categoryId;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public Integer getPriority() {
return priority;
}
public void setPriority(Integer priority) {
this.priority = priority;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
}
package cn.yunliyunwai.beyondclouds.data.model;
import lombok.Data;
@Data
public class Result<T> {
private Integer code;
private String msg;
private T data;
public Result(Integer code, String msg, T data) {
this.code = code;
this.msg = msg;
this.data = data;
}
public static <T> Result<T> ok(T data) {
return new Result<>(0, "success", data);
}
}
package cn.yunliyunwai.beyondclouds.data.source;
import androidx.lifecycle.LiveData;
import java.util.List;
import cn.yunliyunwai.beyondclouds.data.model.Blog;
import cn.yunliyunwai.beyondclouds.data.model.BlogCategory;
import cn.yunliyunwai.beyondclouds.data.model.Page;
import cn.yunliyunwai.beyondclouds.data.model.Result;
public interface IBlogRepository {
/**
* 获取博客类别
* @return
*/
LiveData<Result<List<BlogCategory>>> fetchCategories();
LiveData<List<BlogCategory>> loadCategoriesFromCache();
LiveData<Result<Page<Blog>>> fetchBlogList(Integer categoryId, Integer page, Integer size);
void makeCategoriesCache(List<BlogCategory> data);
LiveData<Result<Page<Blog>>> fetchHotBlogList(Integer page, int pageSize);
}
package cn.yunliyunwai.beyondclouds.data.source;
import androidx.lifecycle.LiveData;
import cn.yunliyunwai.beyondclouds.data.model.Page;
import cn.yunliyunwai.beyondclouds.data.model.Post;
import cn.yunliyunwai.beyondclouds.data.model.Result;
public interface IPostRepository {
LiveData<Result<Page<Post>>> getLatestPostList(Integer type, Integer page, int pageSize);
LiveData<Result<Page<Post>>> getRecommendPostList(Integer page, int pageSize);
}
package cn.yunliyunwai.beyondclouds.data.source;
import androidx.lifecycle.LiveData;
import java.util.List;
import cn.yunliyunwai.beyondclouds.data.model.Page;
import cn.yunliyunwai.beyondclouds.data.model.Project;
import cn.yunliyunwai.beyondclouds.data.model.ProjectCategory;
import cn.yunliyunwai.beyondclouds.data.model.Result;
public interface IProjectRepository {
/**
* 获取博客类别
* @return
*/
LiveData<Result<List<ProjectCategory>>> fetchCategories();
LiveData<List<ProjectCategory>> loadCategoriesFromCache();
LiveData<Result<Page<Project>>> fetchProjectList(Integer categoryId, Integer page, Integer size);
void makeCategoriesCache(List<ProjectCategory> categories);
LiveData<Result<Page<Project>>> fetchHotProjectList(Integer page, int pageSize);
}
package cn.yunliyunwai.beyondclouds.data.source;
import androidx.lifecycle.LiveData;
import java.util.List;
import cn.yunliyunwai.beyondclouds.data.model.Page;
import cn.yunliyunwai.beyondclouds.data.model.Question;
import cn.yunliyunwai.beyondclouds.data.model.QuestionCategory;
import cn.yunliyunwai.beyondclouds.data.model.Result;
public interface IQuestionRepository {
/**
* 获取博客类别
* @return
*/
LiveData<Result<List<QuestionCategory>>> fetchCategories();
LiveData<List<QuestionCategory>> loadCategoriesFromCache();
LiveData<Result<Page<Question>>> fetchQuestionList(Integer categoryId, Integer page, Integer size);
void makeCategoriesCache(List<QuestionCategory> categories);
LiveData<Result<Page<Question>>> fetchHotQuestionList(Integer page, int pageSize);
}
package cn.yunliyunwai.beyondclouds.data.source.impl;
import androidx.lifecycle.LiveData;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;
import cn.yunliyunwai.beyondclouds.data.model.Blog;
import cn.yunliyunwai.beyondclouds.data.model.BlogCategory;
import cn.yunliyunwai.beyondclouds.data.model.Page;
import cn.yunliyunwai.beyondclouds.data.model.Result;
import cn.yunliyunwai.beyondclouds.data.source.IBlogRepository;
import cn.yunliyunwai.beyondclouds.data.source.local.database.BlogDatabase;
import cn.yunliyunwai.beyondclouds.data.source.remote.BlogApiStore;
@Singleton
public class BlogRepositoryImpl implements IBlogRepository {
private BlogDatabase blogDatabase;
private BlogApiStore blogApiStore;
@Inject
public BlogRepositoryImpl(BlogDatabase blogDatabase, BlogApiStore blogApiStore) {
this.blogDatabase = blogDatabase;
this.blogApiStore = blogApiStore;
}
@Override
public LiveData<Result<List<BlogCategory>>> fetchCategories() {
return blogApiStore.getCategories();
}
@Override
public LiveData<List<BlogCategory>> loadCategoriesFromCache() {
return blogDatabase.blogDao().getCategories();
}
@Override
public LiveData<Result<Page<Blog>>> fetchBlogList(Integer categoryId, Integer page, Integer size) {
return blogApiStore.getBlogList(categoryId, page, size);
}
@Override
public void makeCategoriesCache(List<BlogCategory> categories) {
blogDatabase.blogDao().saveCategories(categories);
}
@Override
public LiveData<Result<Page<Blog>>> fetchHotBlogList(Integer page, int pageSize) {
return blogApiStore.getHotBlogList(page, pageSize);
}
}
package cn.yunliyunwai.beyondclouds.data.source.impl;
import androidx.lifecycle.LiveData;
import javax.inject.Inject;
import javax.inject.Singleton;
import cn.yunliyunwai.beyondclouds.data.model.Page;
import cn.yunliyunwai.beyondclouds.data.model.Post;
import cn.yunliyunwai.beyondclouds.data.model.Result;
import cn.yunliyunwai.beyondclouds.data.source.IPostRepository;
import cn.yunliyunwai.beyondclouds.data.source.remote.PostApiStore;
@Singleton
public class PostRepositoryImpl implements IPostRepository {
PostApiStore postApiStore;
@Inject
public PostRepositoryImpl(PostApiStore postApiStore) {
this.postApiStore = postApiStore;
}
@Override
public LiveData<Result<Page<Post>>> getLatestPostList(Integer type, Integer page, int pageSize) {
return postApiStore.getLatestPostList(type, page, pageSize);
}
@Override
public LiveData<Result<Page<Post>>> getRecommendPostList(Integer page, int pageSize) {
return postApiStore.getRecommendBlogList(page, pageSize);
}
}
package cn.yunliyunwai.beyondclouds.data.source.impl;
import androidx.lifecycle.LiveData;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;
import cn.yunliyunwai.beyondclouds.data.model.Page;
import cn.yunliyunwai.beyondclouds.data.model.Project;
import cn.yunliyunwai.beyondclouds.data.model.ProjectCategory;
import cn.yunliyunwai.beyondclouds.data.model.Result;
import cn.yunliyunwai.beyondclouds.data.source.IProjectRepository;
import cn.yunliyunwai.beyondclouds.data.source.local.database.ProjectDatabase;
import cn.yunliyunwai.beyondclouds.data.source.remote.ProjectApiStore;
@Singleton
public class ProjectRepositoryImpl implements IProjectRepository {
private ProjectDatabase projectDatabase;
private ProjectApiStore projectApiStore;
@Inject
public ProjectRepositoryImpl(ProjectDatabase projectDatabase, ProjectApiStore projectApiStore) {
this.projectDatabase = projectDatabase;
this.projectApiStore = projectApiStore;
}
@Override
public LiveData<Result<List<ProjectCategory>>> fetchCategories() {
return projectApiStore.getCategories();
}
@Override
public LiveData<List<ProjectCategory>> loadCategoriesFromCache() {
return projectDatabase.projectDao().getCategories();
}
@Override
public LiveData<Result<Page<Project>>> fetchProjectList(Integer categoryId, Integer page, Integer size) {
return projectApiStore.getProjectList(categoryId, page, size);
}
@Override
public void makeCategoriesCache(List<ProjectCategory> categories) {
projectDatabase.projectDao().saveCategories(categories);
}
@Override
public LiveData<Result<Page<Project>>> fetchHotProjectList(Integer page, int pageSize) {
return projectApiStore.getHotProjectList(page, pageSize);
}
}
package cn.yunliyunwai.beyondclouds.data.source.impl;
import androidx.lifecycle.LiveData;
import java.util.List;
import javax.inject.Inject;
import cn.yunliyunwai.beyondclouds.data.model.Page;
import cn.yunliyunwai.beyondclouds.data.model.Question;
import cn.yunliyunwai.beyondclouds.data.model.QuestionCategory;
import cn.yunliyunwai.beyondclouds.data.model.Result;
import cn.yunliyunwai.beyondclouds.data.source.IQuestionRepository;
import cn.yunliyunwai.beyondclouds.data.source.local.database.QuestionDatabase;
import cn.yunliyunwai.beyondclouds.data.source.remote.QuestionApiStore;
public class QuestionRepositoryImpl implements IQuestionRepository {
private QuestionDatabase questionDatabase;
private QuestionApiStore questionApiStore;
@Inject
public QuestionRepositoryImpl(QuestionDatabase questionDatabase, QuestionApiStore questionApiStore) {
this.questionDatabase = questionDatabase;
this.questionApiStore = questionApiStore;
}
@Override
public LiveData<Result<List<QuestionCategory>>> fetchCategories() {
return questionApiStore.getCategories();
}
@Override
public LiveData<List<QuestionCategory>> loadCategoriesFromCache() {
return questionDatabase.questionDao().getCategories();
}
@Override
public LiveData<Result<Page<Question>>> fetchQuestionList(Integer categoryId, Integer page, Integer size) {
return questionApiStore.getQuestionList(categoryId, page, size);
}
@Override
public void makeCategoriesCache(List<QuestionCategory> categories) {
questionDatabase.questionDao().saveCategories(categories);
}
@Override
public LiveData<Result<Page<Question>>> fetchHotQuestionList(Integer page, int pageSize) {
return questionApiStore.getHotQuestionList(page, pageSize);
}
}
package cn.yunliyunwai.beyondclouds.data.source.local.dao;
import androidx.lifecycle.LiveData;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import androidx.room.Query;
import java.util.List;
import cn.yunliyunwai.beyondclouds.data.model.BlogCategory;
@Dao
public interface BlogDao {
@Query("SELECT * FROM BlogCategory order by priority desc, create_time asc")
LiveData<List<BlogCategory>> getCategories();
@Insert(onConflict = OnConflictStrategy.REPLACE)
void saveCategories(List<BlogCategory> categories);
}
package cn.yunliyunwai.beyondclouds.data.source.local.dao;
import androidx.lifecycle.LiveData;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import androidx.room.Query;
import java.util.List;
import cn.yunliyunwai.beyondclouds.data.model.ProjectCategory;
@Dao
public interface ProjectDao {
@Query("SELECT * FROM ProjectCategory order by priority desc, create_time asc")
LiveData<List<ProjectCategory>> getCategories();
@Insert(onConflict = OnConflictStrategy.REPLACE)
void saveCategories(List<ProjectCategory> categories);
}
package cn.yunliyunwai.beyondclouds.data.source.local.dao;
import androidx.lifecycle.LiveData;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import androidx.room.Query;
import java.util.List;
import cn.yunliyunwai.beyondclouds.data.model.QuestionCategory;
@Dao
public interface QuestionDao {
@Query("SELECT * FROM QuestionCategory order by priority desc, create_time asc")
LiveData<List<QuestionCategory>> getCategories();
@Insert(onConflict = OnConflictStrategy.REPLACE)
void saveCategories(List<QuestionCategory> categories);
}
package cn.yunliyunwai.beyondclouds.data.source.local.database;
import androidx.room.Database;
import androidx.room.RoomDatabase;
import cn.yunliyunwai.beyondclouds.data.model.BlogCategory;
import cn.yunliyunwai.beyondclouds.data.source.local.dao.BlogDao;
@Database(entities = {BlogCategory.class}, version = 1, exportSchema = false)
public abstract class BlogDatabase extends RoomDatabase {
public abstract BlogDao blogDao();
}
package cn.yunliyunwai.beyondclouds.data.source.local.database;
import androidx.room.Database;
import androidx.room.RoomDatabase;
import cn.yunliyunwai.beyondclouds.data.model.ProjectCategory;
import cn.yunliyunwai.beyondclouds.data.source.local.dao.ProjectDao;
@Database(entities = {ProjectCategory.class}, version = 1, exportSchema = false)
public abstract class ProjectDatabase extends RoomDatabase {
public abstract ProjectDao projectDao();
}
package cn.yunliyunwai.beyondclouds.data.source.local.database;
import androidx.room.Database;
import androidx.room.RoomDatabase;
import cn.yunliyunwai.beyondclouds.data.model.QuestionCategory;
import cn.yunliyunwai.beyondclouds.data.source.local.dao.QuestionDao;
@Database(entities = {QuestionCategory.class}, version = 1, exportSchema = false)
public abstract class QuestionDatabase extends RoomDatabase {
public abstract QuestionDao questionDao();
}
package cn.yunliyunwai.beyondclouds.data.source.remote;
import androidx.lifecycle.LiveData;
import java.util.List;
import cn.yunliyunwai.beyondclouds.data.model.Blog;
import cn.yunliyunwai.beyondclouds.data.model.BlogCategory;
import cn.yunliyunwai.beyondclouds.data.model.Page;
import cn.yunliyunwai.beyondclouds.data.model.Result;
import retrofit2.http.GET;
import retrofit2.http.Query;
public interface BlogApiStore {
@GET("blog/categories")
LiveData<Result<List<BlogCategory>>> getCategories();
@GET("blogs")
LiveData<Result<Page<Blog>>> getBlogList(@Query("categoryId") Integer categoryId,
@Query("page") Integer page,
@Query("size") Integer size);
@GET("blog/hots")
LiveData<Result<Page<Blog>>> getHotBlogList( @Query("page") Integer page,
@Query("size") int pageSize);
}
package cn.yunliyunwai.beyondclouds.data.source.remote;
import androidx.lifecycle.LiveData;
import cn.yunliyunwai.beyondclouds.data.model.Page;
import cn.yunliyunwai.beyondclouds.data.model.Post;
import cn.yunliyunwai.beyondclouds.data.model.Result;
import retrofit2.http.GET;
import retrofit2.http.Query;
public interface PostApiStore {
/**
* 推荐动态
* @param page
* @param pageSize
* @return
*/
@GET("post/recommends")
LiveData<Result<Page<Post>>> getRecommendBlogList(@Query("page") Integer page,
@Query("size") int pageSize);
/**
* 最新动态
* @param page
* @param pageSize
* @return
*/
@GET("posts")
LiveData<Result<Page<Post>>> getLatestPostList(@Query("type") Integer type, @Query("page") Integer page,
@Query("size") int pageSize);
}
package cn.yunliyunwai.beyondclouds.data.source.remote;
import androidx.lifecycle.LiveData;
import java.util.List;
import cn.yunliyunwai.beyondclouds.data.model.Page;
import cn.yunliyunwai.beyondclouds.data.model.Project;
import cn.yunliyunwai.beyondclouds.data.model.ProjectCategory;
import cn.yunliyunwai.beyondclouds.data.model.Result;
import retrofit2.http.GET;
import retrofit2.http.Query;
public interface ProjectApiStore {
@GET("project/categories")
LiveData<Result<List<ProjectCategory>>> getCategories();
@GET("projects")
LiveData<Result<Page<Project>>> getProjectList(@Query("categoryId") Integer categoryId,
@Query("page") Integer page,
@Query("size") Integer size);
@GET("project/hots")
LiveData<Result<Page<Project>>> getHotProjectList(@Query("page") Integer page,
@Query("size") int pageSize);
}
package cn.yunliyunwai.beyondclouds.data.source.remote;
import androidx.lifecycle.LiveData;
import java.util.List;
import cn.yunliyunwai.beyondclouds.data.model.Page;
import cn.yunliyunwai.beyondclouds.data.model.Question;
import cn.yunliyunwai.beyondclouds.data.model.QuestionCategory;
import cn.yunliyunwai.beyondclouds.data.model.Result;
import retrofit2.http.GET;
import retrofit2.http.Query;
public interface QuestionApiStore {
@GET("question/categories")
LiveData<Result<List<QuestionCategory>>> getCategories();
@GET("questions")
LiveData<Result<Page<Question>>> getQuestionList(@Query("categoryId") Integer categoryId,
@Query("page") Integer page,
@Query("size") Integer size);
@GET("question/hots")
LiveData<Result<Page<Question>>> getHotQuestionList(@Query("page") Integer page,
@Query("size") int pageSize);
}
package cn.yunliyunwai.beyondclouds.di.component;
import android.content.Context;
import javax.inject.Singleton;
import cn.yunliyunwai.beyondclouds.MainActivity;
import cn.yunliyunwai.beyondclouds.di.module.AppModule;
import cn.yunliyunwai.beyondclouds.di.module.AppModuleBinds;
import cn.yunliyunwai.beyondclouds.di.module.BlogListFragmentModuleBinds;
import cn.yunliyunwai.beyondclouds.di.module.DynamicFragmentModuleBinds;
import cn.yunliyunwai.beyondclouds.di.module.DynamicListFragmentModuleBinds;
import cn.yunliyunwai.beyondclouds.di.module.HomeFragmentModuleBinds;
import cn.yunliyunwai.beyondclouds.di.module.ProjectFragmentModuleBinds;
import cn.yunliyunwai.beyondclouds.di.module.ProjectListFragmentModuleBinds;
import cn.yunliyunwai.beyondclouds.di.module.QAFragmentModuleBinds;
import cn.yunliyunwai.beyondclouds.di.module.QuestionListFragmentModuleBinds;
import cn.yunliyunwai.beyondclouds.di.module.SplashActivityModuleBinds;
import cn.yunliyunwai.beyondclouds.di.module.ViewModelFactory;
import dagger.BindsInstance;
import dagger.Component;
@Singleton
@Component(modules = {
AppModule.class,
AppModuleBinds.class,
ViewModelFactory.ViewModelBuilderModule.class,
HomeFragmentModuleBinds.class,
ProjectFragmentModuleBinds.class,
DynamicFragmentModuleBinds.class,
QAFragmentModuleBinds.class,
BlogListFragmentModuleBinds.class,
ProjectListFragmentModuleBinds.class,
DynamicListFragmentModuleBinds.class,
QuestionListFragmentModuleBinds.class,
SplashActivityModuleBinds.class
})
public interface AppComponent {
@Component.Factory
interface Factory {
AppComponent create(@BindsInstance Context context);
}
HomeFragmentComponent.Factory homeFragmentComponent();
ProjectFragmentComponent.Factory projectFragmentComponent();
DynamicFragmentComponent.Factory dynamicFragmentComponent();
QAFragmentComponent.Factory qAFragmentComponent();
SplashActivityComponent.Factory splashActivityComponent();
BlogListFragmentComponent.Factory blogListFragmentComponent();
ProjectListFragmentComponent.Factory projectListFragmentComponent();
DynamicListFragmentComponent.Factory dynamicListFragmentComponent();
QuestionListFragmentComponent.Factory questionListFragmentComponent();
void inject(MainActivity mainActivity);
}
package cn.yunliyunwai.beyondclouds.di.component;
import cn.yunliyunwai.beyondclouds.ui.HomeFragment;
import dagger.Subcomponent;
@Subcomponent
public interface BlogListFragmentComponent {
@Subcomponent.Factory
interface Factory {
BlogListFragmentComponent create();
}
void inject(HomeFragment.BlogListFragment blogListFragment);
}
package cn.yunliyunwai.beyondclouds.di.component;
import cn.yunliyunwai.beyondclouds.ui.DynamicFragment;
import dagger.Subcomponent;
@Subcomponent
public interface DynamicFragmentComponent {
@Subcomponent.Factory
interface Factory {
DynamicFragmentComponent create();
}
void inject(DynamicFragment dynamicFragment);
}
package cn.yunliyunwai.beyondclouds.di.component;
import cn.yunliyunwai.beyondclouds.ui.DynamicFragment;
import dagger.Subcomponent;
@Subcomponent
public interface DynamicListFragmentComponent {
@Subcomponent.Factory
interface Factory {
DynamicListFragmentComponent create();
}
void inject(DynamicFragment.DynamicListFragment dynamicListFragment);
}
package cn.yunliyunwai.beyondclouds.di.component;
import cn.yunliyunwai.beyondclouds.ui.HomeFragment;
import dagger.Subcomponent;
@Subcomponent
public interface HomeFragmentComponent {
@Subcomponent.Factory
interface Factory {
HomeFragmentComponent create();
}
void inject(HomeFragment homeFragment);
}
package cn.yunliyunwai.beyondclouds.di.component;
import cn.yunliyunwai.beyondclouds.ui.ProjectFragment;
import dagger.Subcomponent;
@Subcomponent
public interface ProjectFragmentComponent {
@Subcomponent.Factory
interface Factory {
ProjectFragmentComponent create();
}
void inject(ProjectFragment projectFragment);
}
package cn.yunliyunwai.beyondclouds.di.component;
import cn.yunliyunwai.beyondclouds.ui.ProjectFragment;
import dagger.Subcomponent;
@Subcomponent
public interface ProjectListFragmentComponent {
@Subcomponent.Factory
interface Factory {
ProjectListFragmentComponent create();
}
void inject(ProjectFragment.ProjectListFragment projectListFragment);
}
package cn.yunliyunwai.beyondclouds.di.component;
import cn.yunliyunwai.beyondclouds.ui.QAFragment;
import dagger.Subcomponent;
@Subcomponent
public interface QAFragmentComponent {
@Subcomponent.Factory
interface Factory {
QAFragmentComponent create();
}
void inject(QAFragment qaFragment);
}
package cn.yunliyunwai.beyondclouds.di.component;
import cn.yunliyunwai.beyondclouds.ui.QAFragment;
import dagger.Subcomponent;
@Subcomponent
public interface QuestionListFragmentComponent {
@Subcomponent.Factory
interface Factory {
QuestionListFragmentComponent create();
}
void inject(QAFragment.QuestionListFragment questionListFragment);
}
package cn.yunliyunwai.beyondclouds.di.component;
import cn.yunliyunwai.beyondclouds.SplashActivity;
import cn.yunliyunwai.beyondclouds.di.scope.ActivityScope;
import dagger.Subcomponent;
@ActivityScope
@Subcomponent
public interface SplashActivityComponent {
@Subcomponent.Factory
interface Factory {
SplashActivityComponent create();
}
void inject(SplashActivity splashActivity);
}
package cn.yunliyunwai.beyondclouds.di.module;
import android.content.Context;
import androidx.room.Room;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import java.lang.reflect.Type;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import javax.inject.Singleton;
import cn.yunliyunwai.beyondclouds.adapter.common.LiveDataCallAdapterFactory;
import cn.yunliyunwai.beyondclouds.data.source.local.database.BlogDatabase;
import cn.yunliyunwai.beyondclouds.data.source.local.database.ProjectDatabase;
import cn.yunliyunwai.beyondclouds.data.source.local.database.QuestionDatabase;
import cn.yunliyunwai.beyondclouds.data.source.remote.BlogApiStore;
import cn.yunliyunwai.beyondclouds.data.source.remote.PostApiStore;
import cn.yunliyunwai.beyondclouds.data.source.remote.ProjectApiStore;
import cn.yunliyunwai.beyondclouds.data.source.remote.QuestionApiStore;
import cn.yunliyunwai.beyondclouds.util.AppExecutors;
import dagger.Module;
import dagger.Provides;
import okhttp3.OkHttpClient;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
@Module
public class AppModule {
@Singleton
@Provides
public BlogApiStore provideBlogRemoteDataSource(Retrofit retrofit) {
return retrofit.create(BlogApiStore.class);
}
@Singleton
@Provides
public ProjectApiStore provideProjectRemoteDataSource(Retrofit retrofit) {
return retrofit.create(ProjectApiStore.class);
}
@Singleton
@Provides
public PostApiStore providePostApiRemoteDataSource(Retrofit retrofit) {
return retrofit.create(PostApiStore.class);
}
@Singleton
@Provides
public QuestionApiStore provideQuestionApiRemoteDataSource(Retrofit retrofit) {
return retrofit.create(QuestionApiStore.class);
}
@Singleton
@Provides
BlogDatabase provideBlogDatabase(Context context) {
return Room
.databaseBuilder(context.getApplicationContext(),
BlogDatabase.class,
"Blog.db").build();
}
@Singleton
@Provides
ProjectDatabase provideProjectDatabase(Context context) {
return Room
.databaseBuilder(context.getApplicationContext(),
ProjectDatabase.class,
"Project.db").build();
}
@Singleton
@Provides
QuestionDatabase provideQuestionDatabase(Context context) {
return Room
.databaseBuilder(context.getApplicationContext(),
QuestionDatabase.class,
"Question.db").build();
}
@Singleton
@Provides
public OkHttpClient provideOkHttpClient() {
OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder()
.connectTimeout(60, TimeUnit.SECONDS);
return clientBuilder.build();
}
@Singleton
@Provides
public Retrofit provideRetrofit(OkHttpClient okHttpClient) {
Gson gson = new GsonBuilder().registerTypeAdapter(Date.class, new JsonDeserializer<Date>() {
public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
return new Date(json.getAsJsonPrimitive().getAsLong());
}
})
.setDateFormat("yyyy-MM-dd HH:mm:ss")
.create();
return new Retrofit.Builder()
.baseUrl("https://api.yunliyunwai.cn/api/")
.client(okHttpClient)
.addCallAdapterFactory(new LiveDataCallAdapterFactory())
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
}
@Singleton
@Provides
public AppExecutors provideAppExecutors() {
return AppExecutors.getInstance();
}
}
package cn.yunliyunwai.beyondclouds.di.module;
import androidx.lifecycle.ViewModel;
import javax.inject.Singleton;
import cn.yunliyunwai.beyondclouds.data.source.IBlogRepository;
import cn.yunliyunwai.beyondclouds.data.source.IPostRepository;
import cn.yunliyunwai.beyondclouds.data.source.IProjectRepository;
import cn.yunliyunwai.beyondclouds.data.source.IQuestionRepository;
import cn.yunliyunwai.beyondclouds.data.source.impl.BlogRepositoryImpl;
import cn.yunliyunwai.beyondclouds.data.source.impl.PostRepositoryImpl;
import cn.yunliyunwai.beyondclouds.data.source.impl.ProjectRepositoryImpl;
import cn.yunliyunwai.beyondclouds.data.source.impl.QuestionRepositoryImpl;
import cn.yunliyunwai.beyondclouds.viewmodel.MainActivityViewModel;
import dagger.Binds;
import dagger.Module;
import dagger.multibindings.IntoMap;
@Module
public interface AppModuleBinds {
@Singleton
@Binds
IBlogRepository bindBlogRepository(BlogRepositoryImpl blogRepository);
@Singleton
@Binds
IProjectRepository bindProjectRepository(ProjectRepositoryImpl projectRepository);
@Singleton
@Binds
IPostRepository bindPostRepository(PostRepositoryImpl postRepository);
@Singleton
@Binds
IQuestionRepository bindQuestionRepository(QuestionRepositoryImpl questionRepository);
@Binds
@IntoMap
@ViewModelFactory.ViewModelKey(MainActivityViewModel.class)
ViewModel bindMainActivityViewModel(MainActivityViewModel mainActivityViewModel);
}
package cn.yunliyunwai.beyondclouds.di.module;
import androidx.lifecycle.ViewModel;
import cn.yunliyunwai.beyondclouds.di.component.BlogListFragmentComponent;
import cn.yunliyunwai.beyondclouds.viewmodel.BlogListFragmentViewModel;
import dagger.Binds;
import dagger.Module;
import dagger.multibindings.IntoMap;
@Module(subcomponents = BlogListFragmentComponent.class)
public interface BlogListFragmentModuleBinds {
@Binds
@IntoMap
@ViewModelFactory.ViewModelKey(BlogListFragmentViewModel.class)
ViewModel bindBlogListFragmentViewModel(BlogListFragmentViewModel blogListFragmentViewModel);
}
package cn.yunliyunwai.beyondclouds.di.module;
import androidx.lifecycle.ViewModel;
import cn.yunliyunwai.beyondclouds.di.component.DynamicFragmentComponent;
import cn.yunliyunwai.beyondclouds.viewmodel.DynamicFragmentViewModel;
import dagger.Binds;
import dagger.Module;
import dagger.multibindings.IntoMap;
@Module(subcomponents = DynamicFragmentComponent.class)
public interface DynamicFragmentModuleBinds {
@Binds
@IntoMap
@ViewModelFactory.ViewModelKey(DynamicFragmentViewModel.class)
ViewModel bindDynamicFragmentViewModel(DynamicFragmentViewModel dynamicFragmentViewModel);
}
package cn.yunliyunwai.beyondclouds.di.module;
import androidx.lifecycle.ViewModel;
import cn.yunliyunwai.beyondclouds.di.component.DynamicListFragmentComponent;
import cn.yunliyunwai.beyondclouds.viewmodel.DynamicListFragmentViewModel;
import dagger.Binds;
import dagger.Module;
import dagger.multibindings.IntoMap;
@Module(subcomponents = DynamicListFragmentComponent.class)
public interface DynamicListFragmentModuleBinds {
@Binds
@IntoMap
@ViewModelFactory.ViewModelKey(DynamicListFragmentViewModel.class)
ViewModel bindDynamicListFragmentViewModel(DynamicListFragmentViewModel dynamicListFragmentViewModel);
}
package cn.yunliyunwai.beyondclouds.di.module;
import androidx.lifecycle.ViewModel;
import cn.yunliyunwai.beyondclouds.di.component.HomeFragmentComponent;
import cn.yunliyunwai.beyondclouds.viewmodel.HomeFragmentViewModel;
import dagger.Binds;
import dagger.Module;
import dagger.multibindings.IntoMap;
@Module(subcomponents = HomeFragmentComponent.class)
public interface HomeFragmentModuleBinds {
@Binds
@IntoMap
@ViewModelFactory.ViewModelKey(HomeFragmentViewModel.class)
ViewModel bindHomeFragmentViewModel(HomeFragmentViewModel homeFragmentViewModel);
}
package cn.yunliyunwai.beyondclouds.di.module;
import androidx.lifecycle.ViewModel;
import cn.yunliyunwai.beyondclouds.di.component.ProjectFragmentComponent;
import cn.yunliyunwai.beyondclouds.viewmodel.ProjectFragmentViewModel;
import dagger.Binds;
import dagger.Module;
import dagger.multibindings.IntoMap;
@Module(subcomponents = ProjectFragmentComponent.class)
public interface ProjectFragmentModuleBinds {
@Binds
@IntoMap
@ViewModelFactory.ViewModelKey(ProjectFragmentViewModel.class)
ViewModel bindProjectFragmentViewModel(ProjectFragmentViewModel projectFragmentViewModel);
}
package cn.yunliyunwai.beyondclouds.di.module;
import androidx.lifecycle.ViewModel;
import cn.yunliyunwai.beyondclouds.di.component.ProjectListFragmentComponent;
import cn.yunliyunwai.beyondclouds.viewmodel.ProjectListFragmentViewModel;
import dagger.Binds;
import dagger.Module;
import dagger.multibindings.IntoMap;
@Module(subcomponents = ProjectListFragmentComponent.class)
public interface ProjectListFragmentModuleBinds {
@Binds
@IntoMap
@ViewModelFactory.ViewModelKey(ProjectListFragmentViewModel.class)
ViewModel bindProjectListFragmentViewModel(ProjectListFragmentViewModel projectListFragmentViewModel);
}
package cn.yunliyunwai.beyondclouds.di.module;
import androidx.lifecycle.ViewModel;
import cn.yunliyunwai.beyondclouds.di.component.QAFragmentComponent;
import cn.yunliyunwai.beyondclouds.viewmodel.QAFragmentViewModel;
import dagger.Binds;
import dagger.Module;
import dagger.multibindings.IntoMap;
@Module(subcomponents = QAFragmentComponent.class)
public interface QAFragmentModuleBinds {
@Binds
@IntoMap
@ViewModelFactory.ViewModelKey(QAFragmentViewModel.class)
ViewModel bindQAFragmentViewModel(QAFragmentViewModel qaFragmentViewModel);
}
package cn.yunliyunwai.beyondclouds.di.module;
import androidx.lifecycle.ViewModel;
import cn.yunliyunwai.beyondclouds.di.component.QuestionListFragmentComponent;
import cn.yunliyunwai.beyondclouds.viewmodel.QuestionListFragmentViewModel;
import dagger.Binds;
import dagger.Module;
import dagger.multibindings.IntoMap;
@Module(subcomponents = QuestionListFragmentComponent.class)
public interface QuestionListFragmentModuleBinds {
@Binds
@IntoMap
@ViewModelFactory.ViewModelKey(QuestionListFragmentViewModel.class)
ViewModel bindQuestionListFragmentViewModel(QuestionListFragmentViewModel questionListFragmentViewModel);
}
package cn.yunliyunwai.beyondclouds.di.module;
import androidx.lifecycle.ViewModel;
import cn.yunliyunwai.beyondclouds.di.component.SplashActivityComponent;
import cn.yunliyunwai.beyondclouds.viewmodel.SplashActivityViewModel;
import dagger.Binds;
import dagger.Module;
import dagger.multibindings.IntoMap;
@Module(subcomponents = SplashActivityComponent.class)
public interface SplashActivityModuleBinds {
@Binds
@IntoMap
@ViewModelFactory.ViewModelKey(SplashActivityViewModel.class)
ViewModel bindSplashActivityViewModel(SplashActivityViewModel splashActivityViewModel);
}
package cn.yunliyunwai.beyondclouds.di.module;
import androidx.annotation.NonNull;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Provider;
import dagger.Binds;
import dagger.MapKey;
import dagger.Module;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
public class ViewModelFactory implements ViewModelProvider.Factory {
private Map<Class<? extends ViewModel>, Provider<ViewModel>> creators;
@Inject
public ViewModelFactory(Map<Class<? extends ViewModel>, Provider<ViewModel>> creators) {
this.creators = creators;
}
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
Provider<ViewModel> creator = creators.get(modelClass);
if (null == creator) {
for (Class<? extends ViewModel> key : creators.keySet()) {
if (modelClass.isAssignableFrom(key)) {
creator = creators.get(key);
break;
}
}
}
if (creator == null) {
throw new IllegalArgumentException("Unknown model class: " + modelClass);
}
try {
return (T) creator.get();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Module
public interface ViewModelBuilderModule {
@Binds
ViewModelProvider.Factory bindViewModelFactory(ViewModelFactory viewModelFactory);
}
@Target(
ElementType.METHOD
)
@Retention(RUNTIME)
@MapKey
@interface ViewModelKey {
Class<? extends ViewModel> value();
}
}
package cn.yunliyunwai.beyondclouds.di.scope;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.inject.Scope;
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityScope {
}
package cn.yunliyunwai.beyondclouds.di.scope;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.inject.Scope;
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface FragmentScope {
}
package cn.yunliyunwai.beyondclouds.ui;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.BaseViewHolder;
import javax.inject.Inject;
import cn.yunliyunwai.beyondclouds.R;
import cn.yunliyunwai.beyondclouds.adapter.DynamicListRecyclerViewAdapter;
import cn.yunliyunwai.beyondclouds.base.BaseFragment;
import cn.yunliyunwai.beyondclouds.data.model.Post;
import cn.yunliyunwai.beyondclouds.databinding.FragmentDynamicBinding;
import cn.yunliyunwai.beyondclouds.ui.common.FragmentFactory;
import cn.yunliyunwai.beyondclouds.ui.common.RefreshableFragment;
import cn.yunliyunwai.beyondclouds.util.CategoryViewpagerUtils;
import cn.yunliyunwai.beyondclouds.util.ContextUtils;
import cn.yunliyunwai.beyondclouds.viewmodel.DynamicFragmentViewModel;
import cn.yunliyunwai.beyondclouds.viewmodel.DynamicListFragmentViewModel;
public class DynamicFragment extends BaseFragment<DynamicFragmentViewModel, FragmentDynamicBinding> {
@Inject
ViewModelProvider.Factory factory;
@Override
protected void initInject() {
ContextUtils.getAppComponent(requireActivity()).dynamicFragmentComponent().create().inject(this);
}
@Override
protected DynamicFragmentViewModel initViewModel() {
return new ViewModelProvider(this, factory).get(DynamicFragmentViewModel.class);
}
@Override
protected FragmentDynamicBinding initDataBinding(@NonNull LayoutInflater inflater, @Nullable ViewGroup container) {
return FragmentDynamicBinding.inflate(inflater, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
CategoryViewpagerUtils.bindViewPager(this, new FragmentFactory() {
@Override
public Fragment create() {
return new DynamicListFragment();
}
}, binding.magicIndicator, binding.viewPager, viewModel, true);
}
public static class DynamicListFragment extends RefreshableFragment<Post, DynamicListFragmentViewModel> {
@Inject
ViewModelProvider.Factory factory;
@Override
protected void initInject() {
ContextUtils.getAppComponent(requireActivity()).dynamicListFragmentComponent().create().inject(this);
}
@Override
protected DynamicListFragmentViewModel initViewModel() {
return new ViewModelProvider(this, factory).get(DynamicListFragmentViewModel.class);
}
@Override
protected BaseQuickAdapter<Post, BaseViewHolder> createAdapter() {
return new DynamicListRecyclerViewAdapter(R.layout.post_item);
}
}
}
package cn.yunliyunwai.beyondclouds.ui;
import android.os.Bundle;
import android.text.InputType;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.BaseViewHolder;
import javax.inject.Inject;
import cn.yunliyunwai.beyondclouds.BeyondApplication;
import cn.yunliyunwai.beyondclouds.R;
import cn.yunliyunwai.beyondclouds.adapter.BlogListRecyclerViewAdapter;
import cn.yunliyunwai.beyondclouds.base.BaseFragment;
import cn.yunliyunwai.beyondclouds.data.model.Blog;
import cn.yunliyunwai.beyondclouds.databinding.FragmentHomeBinding;
import cn.yunliyunwai.beyondclouds.ui.common.FragmentFactory;
import cn.yunliyunwai.beyondclouds.ui.common.RefreshableFragment;
import cn.yunliyunwai.beyondclouds.util.CategoryViewpagerUtils;
import cn.yunliyunwai.beyondclouds.util.ContextUtils;
import cn.yunliyunwai.beyondclouds.viewmodel.BlogListFragmentViewModel;
import cn.yunliyunwai.beyondclouds.viewmodel.HomeFragmentViewModel;
public class HomeFragment extends BaseFragment<HomeFragmentViewModel, FragmentHomeBinding> {
@Inject
ViewModelProvider.Factory factory;
@Override
protected void initInject() {
((BeyondApplication)(requireActivity().getApplication())).getAppComponent().homeFragmentComponent().create().inject(this);
}
@Override
protected HomeFragmentViewModel initViewModel() {
return new ViewModelProvider(this, factory).get(HomeFragmentViewModel.class);
}
@Override
protected FragmentHomeBinding initDataBinding(@NonNull LayoutInflater inflater, @Nullable ViewGroup container) {
return FragmentHomeBinding.inflate(inflater, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
CategoryViewpagerUtils.bindViewPager(this, new FragmentFactory() {
@Override
public Fragment create() {
return new BlogListFragment();
}
}, binding.magicIndicator, binding.viewPager, viewModel, false);
binding.searchBar.setInputType(InputType.TYPE_NULL);
binding.searchBar.setInputFocusListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (!hasFocus) {
return;
}
Toast.makeText(v.getContext(), "点击了搜索框", Toast.LENGTH_SHORT).show();
v.clearFocus();
}
});
}
public static class BlogListFragment extends RefreshableFragment<Blog, BlogListFragmentViewModel> {
@Inject
ViewModelProvider.Factory factory;
@Override
protected void initInject() {
ContextUtils.getAppComponent(requireActivity()).blogListFragmentComponent().create().inject(this);
}
@Override
protected BlogListFragmentViewModel initViewModel() {
return new ViewModelProvider(this, factory).get(BlogListFragmentViewModel.class);
}
@Override
protected BaseQuickAdapter<Blog, BaseViewHolder> createAdapter() {
return new BlogListRecyclerViewAdapter(R.layout.blog_item);
}
}
}
package cn.yunliyunwai.beyondclouds.ui;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import cn.yunliyunwai.beyondclouds.base.BaseFragment;
import cn.yunliyunwai.beyondclouds.databinding.FragmentMyBinding;
import cn.yunliyunwai.beyondclouds.util.ViewModelUtils;
import cn.yunliyunwai.beyondclouds.viewmodel.MyFragmentViewModel;
public class MyFragment extends BaseFragment<MyFragmentViewModel, FragmentMyBinding> {
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
@Override
protected void initInject() {
}
@Override
protected MyFragmentViewModel initViewModel() {
return ViewModelUtils.createViewModel(this, MyFragmentViewModel.factory, MyFragmentViewModel.class);
}
@Override
protected FragmentMyBinding initDataBinding(@NonNull LayoutInflater inflater, @Nullable ViewGroup container) {
return FragmentMyBinding.inflate(inflater, container, false);
}
}
package cn.yunliyunwai.beyondclouds.ui;
import android.os.Bundle;
import android.text.InputType;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.BaseViewHolder;
import javax.inject.Inject;
import cn.yunliyunwai.beyondclouds.BeyondApplication;
import cn.yunliyunwai.beyondclouds.R;
import cn.yunliyunwai.beyondclouds.adapter.ProjectListRecyclerViewAdapter;
import cn.yunliyunwai.beyondclouds.base.BaseFragment;
import cn.yunliyunwai.beyondclouds.data.model.Project;
import cn.yunliyunwai.beyondclouds.databinding.FragmentProjectBinding;
import cn.yunliyunwai.beyondclouds.ui.common.FragmentFactory;
import cn.yunliyunwai.beyondclouds.ui.common.RefreshableFragment;
import cn.yunliyunwai.beyondclouds.util.CategoryViewpagerUtils;
import cn.yunliyunwai.beyondclouds.util.ContextUtils;
import cn.yunliyunwai.beyondclouds.viewmodel.ProjectFragmentViewModel;
import cn.yunliyunwai.beyondclouds.viewmodel.ProjectListFragmentViewModel;
public class ProjectFragment extends BaseFragment<ProjectFragmentViewModel, FragmentProjectBinding> {
@Inject
ViewModelProvider.Factory factory;
@Override
protected void initInject() {
((BeyondApplication)(requireActivity().getApplication())).getAppComponent().projectFragmentComponent().create().inject(this);
}
@Override
protected ProjectFragmentViewModel initViewModel() {
return new ViewModelProvider(this, factory).get(ProjectFragmentViewModel.class);
}
@Override
protected FragmentProjectBinding initDataBinding(@NonNull LayoutInflater inflater, @Nullable ViewGroup container) {
return FragmentProjectBinding.inflate(inflater, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
CategoryViewpagerUtils.bindViewPager(this, new FragmentFactory() {
@Override
public Fragment create() {
return new ProjectListFragment();
}
}, binding.magicIndicator, binding.viewPager, viewModel, false);
binding.searchBar.setInputType(InputType.TYPE_NULL);
binding.searchBar.setInputFocusListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (!hasFocus) {
return;
}
Toast.makeText(v.getContext(), "点击了搜索框", Toast.LENGTH_SHORT).show();
v.clearFocus();
}
});
}
public static class ProjectListFragment extends RefreshableFragment<Project, ProjectListFragmentViewModel> {
@Inject
ViewModelProvider.Factory factory;
@Override
protected void initInject() {
ContextUtils.getAppComponent(requireActivity()).projectListFragmentComponent().create().inject(this);
}
@Override
protected ProjectListFragmentViewModel initViewModel() {
return new ViewModelProvider(this, factory).get(ProjectListFragmentViewModel.class);
}
@Override
protected BaseQuickAdapter<Project, BaseViewHolder> createAdapter() {
return new ProjectListRecyclerViewAdapter(R.layout.project_item);
}
}
}
package cn.yunliyunwai.beyondclouds.ui;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.BaseViewHolder;
import javax.inject.Inject;
import cn.yunliyunwai.beyondclouds.R;
import cn.yunliyunwai.beyondclouds.adapter.QuestionListRecyclerViewAdapter;
import cn.yunliyunwai.beyondclouds.base.BaseFragment;
import cn.yunliyunwai.beyondclouds.data.model.Question;
import cn.yunliyunwai.beyondclouds.databinding.FragmentQaBinding;
import cn.yunliyunwai.beyondclouds.ui.common.FragmentFactory;
import cn.yunliyunwai.beyondclouds.ui.common.RefreshableFragment;
import cn.yunliyunwai.beyondclouds.util.CategoryViewpagerUtils;
import cn.yunliyunwai.beyondclouds.util.ContextUtils;
import cn.yunliyunwai.beyondclouds.util.ViewModelUtils;
import cn.yunliyunwai.beyondclouds.viewmodel.QAFragmentViewModel;
import cn.yunliyunwai.beyondclouds.viewmodel.QuestionListFragmentViewModel;
public class QAFragment extends BaseFragment<QAFragmentViewModel, FragmentQaBinding> {
@Inject
ViewModelProvider.Factory factory;
@Override
protected void initInject() {
ContextUtils.getAppComponent(requireActivity()).qAFragmentComponent().create().inject(this);
}
@Override
protected QAFragmentViewModel initViewModel() {
return ViewModelUtils.createViewModel(this, factory, QAFragmentViewModel.class);
}
@Override
protected FragmentQaBinding initDataBinding(@NonNull LayoutInflater inflater, @Nullable ViewGroup container) {
return FragmentQaBinding.inflate(inflater, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
CategoryViewpagerUtils.bindViewPager(this, new FragmentFactory() {
@Override
public Fragment create() {
return new QuestionListFragment();
}
}, binding.magicIndicator, binding.viewPager, viewModel, false);
}
public static class QuestionListFragment extends RefreshableFragment<Question, QuestionListFragmentViewModel> {
@Inject
ViewModelProvider.Factory factory;
@Override
protected BaseQuickAdapter<Question, BaseViewHolder> createAdapter() {
return new QuestionListRecyclerViewAdapter(R.layout.question_item);
}
@Override
protected void initInject() {
ContextUtils.getAppComponent(requireActivity()).questionListFragmentComponent().create().inject(this);
}
@Override
protected QuestionListFragmentViewModel initViewModel() {
return new ViewModelProvider(this, factory).get(QuestionListFragmentViewModel.class);
}
}
}
package cn.yunliyunwai.beyondclouds.ui.common;
import androidx.fragment.app.Fragment;
public interface FragmentFactory<T extends Fragment> {
T create();
}
package cn.yunliyunwai.beyondclouds.ui.common;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.BaseViewHolder;
import java.util.List;
import javax.inject.Inject;
import cn.yunliyunwai.beyondclouds.base.BaseFragment;
import cn.yunliyunwai.beyondclouds.data.model.CommonCategory;
import cn.yunliyunwai.beyondclouds.databinding.SwipeRefreshListBinding;
import cn.yunliyunwai.beyondclouds.view.CustomLoadMoreView;
import cn.yunliyunwai.beyondclouds.viewmodel.common.PageableViewModel;
public abstract class RefreshableFragment<T, VM extends PageableViewModel<T>> extends BaseFragment<VM, SwipeRefreshListBinding> implements SwipeRefreshLayout.OnRefreshListener {
public interface Factory {
<T, VM extends PageableViewModel<T>> RefreshableFragment<T, VM> create();
}
private CommonCategory category;
private BaseQuickAdapter<T, BaseViewHolder> recyclerViewAdapter;
@Inject
ViewModelProvider.Factory factory;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (null == getArguments()) {
category = getArguments().getParcelable("category");
throw new IllegalArgumentException("请传入category参数!");
}
if (null != getArguments()) {
category = getArguments().getParcelable("category");
if (null == category) {
throw new IllegalArgumentException("请传入category参数!");
}
}
}
@Override
protected SwipeRefreshListBinding initDataBinding(@NonNull LayoutInflater inflater, @Nullable ViewGroup container) {
return SwipeRefreshListBinding.inflate(inflater, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
binding.recyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
recyclerViewAdapter = createAdapter();
viewModel.getDataList().observe(this, new Observer<List<T>>() {
@Override
public void onChanged(List<T> dataList) {
switch (viewModel.getPageLoadState()) {
case FIRST_LOAD:
recyclerViewAdapter.setNewData(dataList);
recyclerViewAdapter.loadMoreComplete();
break;
case LOAD_SUCCESS:
recyclerViewAdapter.addData(dataList);
recyclerViewAdapter.loadMoreComplete();
break;
case LOAD_END:
recyclerViewAdapter.loadMoreEnd();
break;
case LOAD_FAIL:
recyclerViewAdapter.loadMoreFail();
break;
}
binding.swipeRefreshLayout.setRefreshing(false);
}
});
recyclerViewAdapter.setLoadMoreView(new CustomLoadMoreView());
recyclerViewAdapter.setOnLoadMoreListener(new BaseQuickAdapter.RequestLoadMoreListener() {
@Override
public void onLoadMoreRequested() {
viewModel.loadPage(category, viewModel.getCurrentPage() + 1);
}
}, binding.recyclerView);
binding.recyclerView.setAdapter(recyclerViewAdapter);
binding.swipeRefreshLayout.setOnRefreshListener(this);
}
@Override
public void onResume() {
super.onResume();
if (viewModel.getCurrentPage() == 0) {
binding.swipeRefreshLayout.setRefreshing(true);
onRefresh();
}
}
@Override
public void onRefresh() {
viewModel.loadFirstPage(category);
}
protected abstract BaseQuickAdapter<T, BaseViewHolder> createAdapter();
}
\ No newline at end of file
package cn.yunliyunwai.beyondclouds.ui.page;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.text.method.HideReturnsTransformationMethod;
import android.text.method.PasswordTransformationMethod;
import android.util.TypedValue;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import cn.yunliyunwai.beyondclouds.R;
import cn.yunliyunwai.beyondclouds.databinding.ActivityLoginBinding;
public class LoginActivity extends AppCompatActivity {
private ActivityLoginBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityLoginBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
changeEditTextSize(binding.loginAccountEditText, binding.loginAccountBorder);
changeEditTextSize(binding.loginPasswordEditText, binding.loginPasswordBorder);
changeEditTextSize(binding.loginAuthCodeEditText, binding.loginAuthCodeBorder);
}
/**
* 改变用户输入前后EditText中字体的大小和边框的样式
* @param editText
* @param textView
*/
private void changeEditTextSize(EditText editText, TextView textView) {
editText.addTextChangedListener(new TextWatcher() {
boolean hint;
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (s.length() == 0) {
hint = true;
textView.setBackgroundResource(R.drawable.background_edit_text_lose_focus);
editText.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
} else {
hint = false;
textView.setBackgroundResource(R.drawable.background_edit_text_on_focus);
editText.setTextSize(TypedValue.COMPLEX_UNIT_SP, 15);
}
}
@Override
public void afterTextChanged(Editable s) {
}
});
}
/**
* show_password_button点击事件,显示密码与隐藏密码
* @param view
*/
public void setPswVisible(View view) {
boolean isPwdVisible = !view.isSelected();
if (isPwdVisible) {
view.setSelected(true);
binding.showPasswordButton.setText(R.string.icon_eye);
HideReturnsTransformationMethod method = HideReturnsTransformationMethod.getInstance();
binding.loginPasswordEditText.setTransformationMethod(method);
} else {
view.setSelected(false);
binding.showPasswordButton.setText(R.string.icon_eye_slash);
PasswordTransformationMethod method = PasswordTransformationMethod.getInstance();
binding.loginPasswordEditText.setTransformationMethod(method);
}
binding.loginPasswordEditText.setSelection(binding.loginPasswordEditText.getText().toString().length());
}
}
package cn.yunliyunwai.beyondclouds.ui.page;
import androidx.appcompat.app.AppCompatActivity;
import android.annotation.SuppressLint;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.text.method.HideReturnsTransformationMethod;
import android.text.method.PasswordTransformationMethod;
import android.util.TypedValue;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import cn.yunliyunwai.beyondclouds.R;
import cn.yunliyunwai.beyondclouds.databinding.ActivityRegisterBinding;
public class RegisterActivity extends AppCompatActivity {
private ActivityRegisterBinding binding;
private Editable lastMobile;
private Editable lastEmail;
@SuppressLint("WrongConstant")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityRegisterBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
changeEditTextSize(binding.registerMobileEditText, binding.registerMobileBorder);
changeEditTextSize(binding.registerAuthCodeEditText, binding.registerAuthCodeBorder);
changeEditTextSize(binding.registerPasswordEditText, binding.registerPasswordBorder);
changeEditTextBorderStyle(binding.registerMobileEditText, binding.registerMobileBorder);
changeEditTextBorderStyle(binding.registerAuthCodeEditText, binding.registerAuthCodeBorder);
changeEditTextBorderStyle(binding.registerPasswordEditText, binding.registerPasswordBorder);
}
/**
* 改变用户输入前后EditText中字体的大小和边框的样式
* @param editText
* @param textView
*/
private void changeEditTextSize(EditText editText, TextView textView) {
editText.addTextChangedListener(new TextWatcher() {
boolean hint;
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (s.length() == 0) {
hint = true;
editText.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
} else {
hint = false;
editText.setTextSize(TypedValue.COMPLEX_UNIT_SP, 15);
}
}
@Override
public void afterTextChanged(Editable s) {
}
});
}
private void changeEditTextBorderStyle(EditText editText, TextView textView) {
editText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
textView.setBackgroundResource(R.drawable.background_edit_text_on_focus);
} else {
textView.setBackgroundResource(R.drawable.background_edit_text_lose_focus);
}
}
});
}
/**
* show_password_button点击事件,显示密码与隐藏密码
* @param view
*/
public void setPswVisible(View view) {
boolean isPwdVisible = !view.isSelected();
if (isPwdVisible) {
view.setSelected(true);
binding.showPasswordButton.setText(R.string.icon_eye);
HideReturnsTransformationMethod method = HideReturnsTransformationMethod.getInstance();
binding.registerPasswordEditText.setTransformationMethod(method);
} else {
view.setSelected(false);
binding.showPasswordButton.setText(R.string.icon_eye_slash);
PasswordTransformationMethod method = PasswordTransformationMethod.getInstance();
binding.registerPasswordEditText.setTransformationMethod(method);
}
binding.registerPasswordEditText.setSelection(binding.registerPasswordEditText.getText().toString().length());
}
@SuppressLint("WrongConstant")
public void mobileRegister(View view) {
binding.mobileRegisterTitle.setTextColor(getResources().getColor(R.color.button_on_focus));
binding.emailRegisterTitle.setTextColor(getResources().getColor(R.color.button_lose_focus));
lastEmail = binding.registerMobileEditText.getText();
binding.registerMobileBorderTitle.setText("手机号:");
binding.registerMobileEditText.setHint("请输入手机号");
binding.registerMobileEditText.setText(lastMobile);
binding.registerPasswordBorder.setVisibility(8);
binding.registerPasswordBorderTitle.setVisibility(8);
binding.registerPasswordEditText.setVisibility(8);
binding.showPasswordButton.setVisibility(8);
binding.registerAuthCodeBorder.setVisibility(0);
binding.registerAuthCodeBorderTitle.setVisibility(0);
binding.registerAuthCodeEditText.setVisibility(0);
binding.registerSendAuthCode.setVisibility(0);
}
@SuppressLint("WrongConstant")
public void emailRegister(View view) {
binding.mobileRegisterTitle.setTextColor(getResources().getColor(R.color.button_lose_focus));
binding.emailRegisterTitle.setTextColor(getResources().getColor(R.color.button_on_focus));
lastMobile = binding.registerMobileEditText.getText();
binding.registerMobileBorderTitle.setText("邮箱:");
binding.registerMobileEditText.setHint("请输入邮箱");
binding.registerMobileEditText.setText(lastEmail);
binding.registerPasswordBorder.setVisibility(0);
binding.registerPasswordBorderTitle.setVisibility(0);
binding.registerPasswordEditText.setVisibility(0);
binding.showPasswordButton.setVisibility(0);
binding.registerAuthCodeBorder.setVisibility(8);
binding.registerAuthCodeBorderTitle.setVisibility(8);
binding.registerAuthCodeEditText.setVisibility(8);
binding.registerSendAuthCode.setVisibility(8);
}
}
package cn.yunliyunwai.beyondclouds.ui.page;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import cn.yunliyunwai.beyondclouds.R;
public class RegisterSuccess extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_register_success);
}
}
package cn.yunliyunwai.beyondclouds.util;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import androidx.annotation.NonNull;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* @author yawei
*/
public class AppExecutors {
private static final String TAG = "AppExecutors";
/**磁盘IO线程池**/
private final ExecutorService diskIO;
/**网络IO线程池**/
private final ExecutorService networkIO;
/**UI线程**/
private final Executor mainThread;
/**定时任务线程池**/
private final ScheduledExecutorService scheduledExecutor;
private volatile static AppExecutors appExecutors;
public static AppExecutors getInstance() {
if (appExecutors == null) {
synchronized (AppExecutors.class) {
if (appExecutors == null) {
appExecutors = new AppExecutors();
}
}
}
return appExecutors;
}
public AppExecutors(ExecutorService diskIO, ExecutorService networkIO, Executor mainThread, ScheduledExecutorService scheduledExecutor) {
this.diskIO = diskIO;
this.networkIO = networkIO;
this.mainThread = mainThread;
this.scheduledExecutor = scheduledExecutor;
}
public AppExecutors() {
this(diskIoExecutor(), networkExecutor(), new MainThreadExecutor(), scheduledThreadPoolExecutor());
}
/**
* 定时(延时)任务线程池
*
* 替代Timer,执行定时任务,延时任务
*/
public ScheduledExecutorService scheduledExecutor() {
return scheduledExecutor;
}
/**
* 磁盘IO线程池(单线程)
*
* 和磁盘操作有关的进行使用此线程(如读写数据库,读写文件)
* 禁止延迟,避免等待
* 此线程不用考虑同步问题
*/
public ExecutorService diskIO() {
return diskIO;
}
/**
* 网络IO线程池
*
* 网络请求,异步任务等适用此线程
* 不建议在这个线程 sleep 或者 wait
*/
public ExecutorService networkIO() {
return networkIO;
}
/**
* UI线程
*
* Android 的MainThread
* UI线程不能做的事情这个都不能做
*/
public Executor mainThread() {
return mainThread;
}
private static ScheduledExecutorService scheduledThreadPoolExecutor() {
return new ScheduledThreadPoolExecutor(16, r -> new Thread(r, "scheduled_executor"), (r, executor) -> Log.e(TAG, "rejectedExecution: scheduled executor queue overflow"));
}
private static ExecutorService diskIoExecutor() {
return new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(1024), r -> new Thread(r, "disk_executor"), (r, executor) -> Log.e(TAG, "rejectedExecution: disk io executor queue overflow"));
}
private static ExecutorService networkExecutor() {
return new ThreadPoolExecutor(3, 6, 1000, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(6), r -> new Thread(r, "network_executor"), (r, executor) -> Log.e(TAG, "rejectedExecution: network executor queue overflow"));
}
private static class MainThreadExecutor implements Executor {
private final Handler mainThreadHandler = new Handler(Looper.getMainLooper());
@Override
public void execute(@NonNull Runnable command) {
mainThreadHandler.post(command);
}
}
}
package cn.yunliyunwai.beyondclouds.util;
import androidx.lifecycle.ViewModel;
import androidx.viewbinding.ViewBinding;
import java.lang.reflect.Method;
public class BindingUtils {
public static boolean bindViewModel(ViewBinding binding, ViewModel viewModel) {
Class<? extends ViewBinding> bindingClass = binding.getClass();
Class<? extends ViewModel> viewModelClass = viewModel.getClass();
try {
Method method = bindingClass.getMethod("setViewModel", viewModelClass);
method.invoke(binding, viewModel);
return true;
} catch (Exception e) {
return false;
}
}
}
package cn.yunliyunwai.beyondclouds.util;
import androidx.viewpager.widget.ViewPager;
import net.lucode.hackware.magicindicator.MagicIndicator;
import net.lucode.hackware.magicindicator.ViewPagerHelper;
import net.lucode.hackware.magicindicator.buildins.commonnavigator.CommonNavigator;
import cn.yunliyunwai.beyondclouds.R;
import cn.yunliyunwai.beyondclouds.adapter.common.CategoryFragmentPagerAdapter;
import cn.yunliyunwai.beyondclouds.adapter.common.CategoryNavigatorAdapter;
import cn.yunliyunwai.beyondclouds.base.BaseActivity;
import cn.yunliyunwai.beyondclouds.base.BaseFragment;
import cn.yunliyunwai.beyondclouds.ui.common.FragmentFactory;
import cn.yunliyunwai.beyondclouds.viewmodel.common.CategoryViewModel;
public class CategoryViewpagerUtils {
public static void bindViewPager(BaseFragment fragment, FragmentFactory factory, MagicIndicator magicIndicator, ViewPager viewPager, CategoryViewModel viewModel, boolean adjustMode) {
CommonNavigator commonNavigator = new CommonNavigator(fragment.requireContext());
commonNavigator.setBackgroundColor(fragment.requireActivity().getResources().getColor(R.color.colorWhite));
commonNavigator.setAdjustMode(adjustMode);
magicIndicator.setNavigator(commonNavigator);
ViewPagerHelper.bind(magicIndicator, viewPager);
viewModel.loadCategories();
viewModel
.getCategories()
.observe(fragment.getViewLifecycleOwner(),
categories -> {
viewPager.setAdapter(new CategoryFragmentPagerAdapter(fragment.getChildFragmentManager(), categories, factory));
commonNavigator.setAdapter(new CategoryNavigatorAdapter(categories, viewPager));
});
}
public static void bindViewPager(BaseActivity activity, FragmentFactory factory, MagicIndicator magicIndicator, ViewPager viewPager, CategoryViewModel viewModel, boolean adjustMode) {
CommonNavigator commonNavigator = new CommonNavigator(activity);
commonNavigator.setBackgroundColor(activity.getResources().getColor(R.color.colorWhite));
commonNavigator.setAdjustMode(adjustMode);
magicIndicator.setNavigator(commonNavigator);
ViewPagerHelper.bind(magicIndicator, viewPager);
viewModel.loadCategories();
viewModel
.getCategories()
.observe(activity,
categories -> {
viewPager.setAdapter(new CategoryFragmentPagerAdapter(activity.getSupportFragmentManager(), categories, factory));
commonNavigator.setAdapter(new CategoryNavigatorAdapter(categories, viewPager));
});
}
}
package cn.yunliyunwai.beyondclouds.util;
import android.app.Activity;
import cn.yunliyunwai.beyondclouds.BeyondApplication;
import cn.yunliyunwai.beyondclouds.di.component.AppComponent;
public class ContextUtils {
public static BeyondApplication getBeyondApplication(Activity activity) {
return (BeyondApplication) activity.getApplication();
}
public static AppComponent getAppComponent(Activity activity) {
return getBeyondApplication(activity).getAppComponent();
}
}
package cn.yunliyunwai.beyondclouds.util;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
public class DateUtils {
private static final SimpleDateFormat simpleDateFormat;
static {
simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
simpleDateFormat.setTimeZone(TimeZone.getTimeZone("GMT+8:00"));
}
public static String format(Date date) {
return simpleDateFormat.format(date);
}
}
package cn.yunliyunwai.beyondclouds.util;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import androidx.annotation.NonNull;
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation;
import com.bumptech.glide.load.resource.bitmap.TransformationUtils;
import java.security.MessageDigest;
public class GlideRoundTransform extends BitmapTransformation {
private static float radius = 0f;
public GlideRoundTransform() {
this(4);
}
public GlideRoundTransform(int dp) {
super();
this.radius = Resources.getSystem().getDisplayMetrics().density * dp;
}
@Override
protected Bitmap transform(@NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) {
//变换的时候裁切
Bitmap bitmap = TransformationUtils.centerCrop(pool, toTransform, outWidth, outHeight);
return roundCrop(pool, bitmap);
}
@Override
public void updateDiskCacheKey(MessageDigest messageDigest) {
}
private static Bitmap roundCrop(BitmapPool pool, Bitmap source) {
if (source == null) {
return null;
}
Bitmap result = pool.get(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);
if (result == null) {
result = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);
}
Canvas canvas = new Canvas(result);
Paint paint = new Paint();
paint.setShader(new BitmapShader(source, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP));
paint.setAntiAlias(true);
RectF rectF = new RectF(0f, 0f, source.getWidth(), source.getHeight());
canvas.drawRoundRect(rectF, radius, radius, paint);
return result;
}
}
\ No newline at end of file
package cn.yunliyunwai.beyondclouds.util;
import android.app.Activity;
import android.app.Dialog;
import android.os.Build;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
public class StatusBarUtils {
//设置Activity对应的顶部状态栏的颜色
public static void setWindowStatusBarColor(Activity activity, int colorResId) {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = activity.getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(activity.getResources().getColor(colorResId));
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);//实现状态栏图标和文字颜色为暗色
}
} catch (Exception e) {
e.printStackTrace();
}
}
//设置Dialog对应的顶部状态栏的颜色
public static void setWindowStatusBarColor(Dialog dialog, int colorResId) {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = dialog.getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(dialog.getContext().getResources().getColor(colorResId));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
package cn.yunliyunwai.beyondclouds.util;
import android.app.Application;
import android.graphics.Typeface;
public class TypeFaces {
public static Typeface SOURCE_HAN_SANS_CN = null;
public static Typeface ICON_FONT = null;
public static void loadTypeFaces(Application ctx) {
SOURCE_HAN_SANS_CN = Typeface.createFromAsset(ctx.getAssets(), "fonts/source_han_sans_cn.ttf");
ICON_FONT = Typeface.createFromAsset(ctx.getAssets(), "fonts/iconfont.ttf");
}
}
package cn.yunliyunwai.beyondclouds.util;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelStoreOwner;
public class ViewModelUtils {
/**
* 创建ViewModel
* @param viewModelStoreOwner
* @param factory
* @param viewModelClass
* @param <T>
* @return
*/
public static <T extends ViewModel> T createViewModel(ViewModelStoreOwner viewModelStoreOwner,
ViewModelProvider.Factory factory,
Class<T> viewModelClass) {
return new ViewModelProvider(viewModelStoreOwner, factory).get(viewModelClass);
}
}
package cn.yunliyunwai.beyondclouds.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Build;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import cn.yunliyunwai.beyondclouds.R;
import cn.yunliyunwai.beyondclouds.databinding.BeyondSearchBarBinding;
public class BeyondSearchBar extends FrameLayout {
BeyondSearchBarBinding binding;
private String searchInputHint;
public BeyondSearchBar(@NonNull Context context) {
super(context);
initView(context);
}
public BeyondSearchBar(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initAttrs(context, attrs);
initView(context);
}
public BeyondSearchBar(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initAttrs(context, attrs);
initView(context);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public BeyondSearchBar(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initAttrs(context, attrs);
initView(context);
}
private void initAttrs(Context context, AttributeSet attrs) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.BeyondSearchBar);
searchInputHint = typedArray.getString(R.styleable.BeyondSearchBar_search_input_hint);
}
private void initView(Context context) {
binding = BeyondSearchBarBinding.inflate(LayoutInflater.from(context), this, true);
if (!TextUtils.isEmpty(searchInputHint)) {
binding.searchBarEditText.setHint(searchInputHint);
}
}
public void setLeftIconClickListener(OnClickListener onClickListener) {
binding.searchIconSearch.setOnClickListener(onClickListener);
}
public void setRightIconClickListener(OnClickListener onClickListener) {
binding.searchIconAdd.setOnClickListener(onClickListener);
}
public void setInputClickListener(OnClickListener onClickListener) {
binding.searchBarEditText.setOnClickListener(onClickListener);
}
public void setInputFocusListener(OnFocusChangeListener onFocusChangeListener) {
binding.searchBarEditText.setOnFocusChangeListener(onFocusChangeListener);
}
public void setInputEnabled(boolean enabled) {
binding.searchBarEditText.setEnabled(enabled);
}
public void setInputType(int inputType) {
binding.searchBarEditText.setInputType(inputType);
}
}
package cn.yunliyunwai.beyondclouds.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Build;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import cn.yunliyunwai.beyondclouds.R;
import cn.yunliyunwai.beyondclouds.databinding.BeyondSettingsItemBinding;
public class BeyondSettingsItem extends FrameLayout {
private String mLeftIcon;
private int mLeftIconColor;
private String mRightIcon;
private int mRightIconColor;
private String mTitle;
private String mSubTitle;
private int mSubTitleTextAppearance;
private int mSubTitleBackground;
private boolean mBottomLineVisibility;
private BeyondSettingsItemBinding binding;
public BeyondSettingsItem(@NonNull Context context) {
super(context);
initView(context);
}
public BeyondSettingsItem(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initAttrs(context, attrs);
initView(context);
}
public BeyondSettingsItem(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initAttrs(context, attrs);
initView(context);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public BeyondSettingsItem(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initAttrs(context, attrs);
initView(context);
}
private void initAttrs(Context context, AttributeSet attrs) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.BeyondSettingsItem);
/**
* 左边图标
*/
mLeftIcon = typedArray.getString(R.styleable.BeyondSettingsItem_leftIcon);
mLeftIconColor = typedArray.getColor(R.styleable.BeyondSettingsItem_leftIconColor, context.getResources().getColor(R.color.colorPrimary));
/**
* 标题
*/
mTitle = typedArray.getString(R.styleable.BeyondSettingsItem_title);
/**
* 子标题
*/
mSubTitle = typedArray.getString(R.styleable.BeyondSettingsItem_subTitle);
mSubTitleBackground = typedArray.getResourceId(R.styleable.BeyondSettingsItem_subTitleBackground, 0);
mSubTitleTextAppearance = typedArray.getResourceId(R.styleable.BeyondSettingsItem_subTitleTextAppearance, 0);
/**
* 右边图标
*/
mRightIcon = typedArray.getString(R.styleable.BeyondSettingsItem_rightIcon);
mRightIconColor = typedArray.getColor(R.styleable.BeyondSettingsItem_rightIconColor, 0xff999999);
/**
* 底部分割线
*/
mBottomLineVisibility = typedArray.getBoolean(R.styleable.BeyondSettingsItem_bottomLineVisibility, true);
}
private void initView(Context context) {
binding = BeyondSettingsItemBinding.inflate(LayoutInflater.from(context), this, true);
if (mSubTitleTextAppearance != 0) {
binding.txtSubtitle.setTextAppearance(context, mSubTitleTextAppearance);
}
if (mSubTitleBackground != 0) {
binding.txtSubtitle.setBackgroundResource(mSubTitleBackground);
}
/**
* 设置左边图标
*/
if (!TextUtils.isEmpty(mLeftIcon)) {
binding.iconLeft.setText(mLeftIcon);
}
binding.iconLeft.setTextColor(mLeftIconColor);
/**
* 设置标题
*/
if (!TextUtils.isEmpty(mSubTitle)) {
binding.txtSubtitle.setText(mSubTitle);
}
/**
* 设置子标题
*/
if (!TextUtils.isEmpty(mTitle)) {
binding.txtTitle.setText(mTitle);
}
/**
* 设置右边图标
*/
if (!TextUtils.isEmpty(mRightIcon)) {
binding.iconRight.setText(mRightIcon);
}
binding.iconRight.setTextColor(mRightIconColor);
/**
* 设置底部分割线
*/
if (!mBottomLineVisibility) {
binding.lineBottom.setVisibility(View.GONE);
}
}
}
package cn.yunliyunwai.beyondclouds.view;
import android.content.Context;
import android.util.AttributeSet;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import cn.yunliyunwai.beyondclouds.util.TypeFaces;
public class BeyondTextIcon extends androidx.appcompat.widget.AppCompatTextView {
public BeyondTextIcon(@NonNull Context context) {
super(context);
initView(context);
}
public BeyondTextIcon(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initAttrs(context, attrs);
initView(context);
}
public BeyondTextIcon(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initAttrs(context, attrs);
initView(context);
}
private void initAttrs(Context context, AttributeSet attrs) {
}
private void initView(Context context) {
setTypeface(TypeFaces.ICON_FONT);
}
}
package cn.yunliyunwai.beyondclouds.view;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import cn.yunliyunwai.beyondclouds.databinding.BeyondToolBarBinding;
public class BeyondToolBar extends FrameLayout {
BeyondToolBarBinding binding;
public BeyondToolBar(@NonNull Context context) {
super(context);
initView(context);
}
public BeyondToolBar(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initAttrs(context, attrs);
initView(context);
}
public BeyondToolBar(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initAttrs(context, attrs);
initView(context);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public BeyondToolBar(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initAttrs(context, attrs);
initView(context);
}
private void initAttrs(Context context, AttributeSet attrs) {
}
private void initView(Context context) {
binding = BeyondToolBarBinding.inflate(LayoutInflater.from(context), this, true);
}
}
package cn.yunliyunwai.beyondclouds.view;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.view.View;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
import net.lucode.hackware.magicindicator.FragmentContainerHelper;
import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IPagerIndicator;
import net.lucode.hackware.magicindicator.buildins.commonnavigator.model.PositionData;
import java.util.List;
public class CommonPagerIndicator extends View implements IPagerIndicator {
public static final int MODE_MATCH_EDGE = 0; // drawable宽度 == title宽度 - 2 * mXOffset
public static final int MODE_WRAP_CONTENT = 1; // drawable宽度 == title内容宽度 - 2 * mXOffset
public static final int MODE_EXACTLY = 2;
private int mMode; // 默认为MODE_MATCH_EDGE模式
private Drawable mIndicatorDrawable;
// 控制动画
private Interpolator mStartInterpolator = new LinearInterpolator();
private Interpolator mEndInterpolator = new LinearInterpolator();
private float mDrawableHeight;
private float mDrawableWidth;
private float mYOffset;
private float mXOffset;
private List<PositionData> mPositionDataList;
private Rect mDrawableRect = new Rect();
public CommonPagerIndicator(Context context) {
super(context);
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (mIndicatorDrawable == null) {
return;
}
if (mPositionDataList == null || mPositionDataList.isEmpty()) {
return;
}
// 计算锚点位置
PositionData current = FragmentContainerHelper.getImitativePositionData(mPositionDataList, position);
PositionData next = FragmentContainerHelper.getImitativePositionData(mPositionDataList, position + 1);
float leftX;
float nextLeftX;
float rightX;
float nextRightX;
if (mMode == MODE_MATCH_EDGE) {
leftX = current.mLeft + mXOffset;
nextLeftX = next.mLeft + mXOffset;
rightX = current.mRight - mXOffset;
nextRightX = next.mRight - mXOffset;
mDrawableRect.top = (int) mYOffset;
mDrawableRect.bottom = (int) (getHeight() - mYOffset);
} else if (mMode == MODE_WRAP_CONTENT) {
leftX = current.mContentLeft + mXOffset;
nextLeftX = next.mContentLeft + mXOffset;
rightX = current.mContentRight - mXOffset;
nextRightX = next.mContentRight - mXOffset;
mDrawableRect.top = (int) (current.mContentTop - mYOffset);
mDrawableRect.bottom = (int) (current.mContentBottom + mYOffset);
} else { // MODE_EXACTLY
leftX = current.mLeft + (current.width() - mDrawableWidth) / 2;
nextLeftX = next.mLeft + (next.width() - mDrawableWidth) / 2;
rightX = current.mLeft + (current.width() + mDrawableWidth) / 2;
nextRightX = next.mLeft + (next.width() + mDrawableWidth) / 2;
mDrawableRect.top = (int) (getHeight() - mDrawableHeight - mYOffset);
mDrawableRect.bottom = (int) (getHeight() - mYOffset);
}
mDrawableRect.left = (int) (leftX + (nextLeftX - leftX) * mStartInterpolator.getInterpolation(positionOffset));
mDrawableRect.right = (int) (rightX + (nextRightX - rightX) * mEndInterpolator.getInterpolation(positionOffset));
mIndicatorDrawable.setBounds(mDrawableRect);
invalidate();
}
@Override
public void onPageSelected(int position) {
}
@Override
public void onPageScrollStateChanged(int state) {
}
@Override
protected void onDraw(Canvas canvas) {
if (mIndicatorDrawable != null) {
mIndicatorDrawable.draw(canvas);
}
}
@Override
public void onPositionDataProvide(List<PositionData> dataList) {
mPositionDataList = dataList;
}
public Drawable getIndicatorDrawable() {
return mIndicatorDrawable;
}
public void setIndicatorDrawable(Drawable indicatorDrawable) {
mIndicatorDrawable = indicatorDrawable;
}
public Interpolator getStartInterpolator() {
return mStartInterpolator;
}
public void setStartInterpolator(Interpolator startInterpolator) {
mStartInterpolator = startInterpolator;
}
public Interpolator getEndInterpolator() {
return mEndInterpolator;
}
public void setEndInterpolator(Interpolator endInterpolator) {
mEndInterpolator = endInterpolator;
}
public int getMode() {
return mMode;
}
public void setMode(int mode) {
if (mode == MODE_EXACTLY || mode == MODE_MATCH_EDGE || mode == MODE_WRAP_CONTENT) {
mMode = mode;
} else {
throw new IllegalArgumentException("mode " + mode + " not supported.");
}
}
public float getDrawableHeight() {
return mDrawableHeight;
}
public void setDrawableHeight(float drawableHeight) {
mDrawableHeight = drawableHeight;
}
public float getDrawableWidth() {
return mDrawableWidth;
}
public void setDrawableWidth(float drawableWidth) {
mDrawableWidth = drawableWidth;
}
public float getYOffset() {
return mYOffset;
}
public void setYOffset(float yOffset) {
mYOffset = yOffset;
}
public float getXOffset() {
return mXOffset;
}
public void setXOffset(float xOffset) {
mXOffset = xOffset;
}
}
\ No newline at end of file
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment