在有api调用时如何在没有代码重复的情况下增加progressBar布局 [英] How to inflate progressBar layout without code duplication when there is an api call
问题描述
我的应用程序中包含以下流程:
I have this flow in my app:
声明端点:
public interface EndPoints {
@GET(Constants.POPULAR)
Call<AllData> getAllData(
@Query("api_key") String apiKey
);
}
翻新服务:
private static EndPoints endPoints = retrofit.create(EndPoints.class);
public static EndPoints getEndpoints() {
return endPoints ;
}
我在视图模型中称呼它:
And I call this inside my view model:
private void getDataFromApi() {
Call<AllData> call = RetrofitService.getEndPoints().getAllData(Constants.API_KEY);
call.enqueue(new Callback<AllData>() {
@Override
public void onResponse(Call<AllData> call, Response<AllData> response) {
}
if (response.isSuccessful()) {
_allData.setValue(response.body());
}
@Override
public void onFailure(Call<AllData> call, Throwable t) {
}
});
}
基本活动:
public abstract class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayoutId());
}
进度条布局(我对每个xml文件进行了更新,而不是使用框架布局,而是创建了一个称为进度条布局的xml,并且希望在每次调用时都对此进行充气):
Progress bar layout(I updated this instead of using frame layout every xml file, I created xml called progress bar layout and I want to inflate this every call):
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/frame_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"/>
</FrameLayout>
如何在每个api调用中设置进度栏?
How can I set progress bar every api call?
最初,我检查了getDataFromApi
是否正在加载,将值设置为bool LiveData
并在我的活动中观察到了这一点.
Initally I checked in getDataFromApi
if is loading or not, set the value into bool LiveData
and observed this inside my activity.
问题在于这是疯狂的代码重复.
The problem with this is it's an insane amount of code duplication.
每个api调用都需要设置加载状态,每个活动都需要观察加载状态并调用View.Visible
和View.Hide
,每个活动xml都需要创建带有进度条的框架布局.
Every api call I need to set loading state, every activity I need to observer the loading state and call View.Visible
and View.Hide
and every activity xml I need to create frame layout with progress bar.
我相信答案是在基本活动内部的某个地方,因为它是唯一可以控制"应用程序中所有活动的地方,但是我想不出解决此问题的方法
I believe the answer is somewhere inside the base activity as it is the only place that can "control" all the activities in the app, but I can't think of a way of solving this problem
推荐答案
- 我找到了一个解决方案,它将代替大量重复的代码,仅要求您在儿童活动"(1行代码)中仅重复一种小方法.没有XML重复.
- 按如下所示更改BaseActivity(忽略我忘记的名字和名字)
解决方案
ParentActivity.java
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.LiveData;
import android.os.Bundle;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ProgressBar;
import com.maproductions.mohamedalaa.stackoverflow_solutions.R;
/**
* Created by <a href="https://github.com/MohamedAlaaEldin636">Mohamed</a> on 6/4/2020.
*/
public abstract class ParentActivity extends AppCompatActivity {
/**
* I didn't see you using data binding that's why this code doesn't have it,
* but it's highly recommended
*/
@Override
final protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_parent);
FrameLayout frameLayout = findViewById(R.id.rootFrameLayout);
ProgressBar progressBar = findViewById(R.id.progressBar);
// Get reference of the child class root view, and add it if wasn't already added
View rootView;
if (frameLayout.getChildCount() == 1) {
rootView = getLayoutInflater().inflate(getLayoutResource(), frameLayout, false);
frameLayout.addView(
rootView,
0,
new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT
)
);
}else {
rootView = frameLayout.getChildAt(0);
}
// Initial state
if (progressBar.getVisibility() == View.VISIBLE) {
rootView.setVisibility(View.GONE);
}
// Child class onCreate code
customOnCreate(rootView, savedInstanceState);
// Observe data changes
getIsLoadingLiveData().observe(this, isLoading -> {
if (isLoading == null || isLoading) {
progressBar.setVisibility(View.VISIBLE);
rootView.setVisibility(View.GONE);
}else {
progressBar.setVisibility(View.GONE);
rootView.setVisibility(View.VISIBLE);
}
});
}
/** Place here the layout resource that you would put in {@link #setContentView(View)} */
@LayoutRes
protected abstract int getLayoutResource();
/**
* Place here the code that you would place in {@link #onCreate(Bundle)},
* And DO NOT CALL {@link #setContentView(View)} it will be auto handled for you
* <br/>
* Also Note this is called before calling {@link #getIsLoadingLiveData()} in case you are
* initialization fields here that are needed to be accessed there.
*/
protected abstract void customOnCreate(@NonNull View rootView, @Nullable Bundle savedInstanceState);
/**
* return a live data value indicating isLoading
*/
@NonNull
protected abstract LiveData<Boolean> getIsLoadingLiveData();
}
@ layout/activity_parent.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/rootFrameLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".view.ParentActivity">
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />
</FrameLayout>
ChildActivity.java
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.ViewModelProviders;
import android.os.Bundle;
import android.view.View;
import com.google.android.material.button.MaterialButton;
import com.maproductions.mohamedalaa.stackoverflow_solutions.R;
import com.maproductions.mohamedalaa.stackoverflow_solutions.view_model.ChildActivityViewModel;
/**
* Created by <a href="https://github.com/MohamedAlaaEldin636">Mohamed</a> on 6/4/2020.
*/
public class ChildActivity extends ParentActivity {
private ChildActivityViewModel viewModel;
@Override
protected int getLayoutResource() {
// Note won't need to add progress bar in this or any other layout.
return R.layout.activity_child;
}
@Override
protected void customOnCreate(@NonNull View rootView, @Nullable Bundle savedInstanceState) {
// Initialize view model
viewModel = ViewModelProviders.of(this).get(ChildActivityViewModel.class);
// Start loading data
viewModel.startLoadingDataFromApi();
// Get references of views and set them up here.
MaterialButton materialButton = rootView.findViewById(R.id.materialButton);
materialButton.setOnClickListener(null);
// Other Views ...
}
// The only duplicated code, but is a must.
@NonNull
@Override
protected LiveData<Boolean> getIsLoadingLiveData() {
return androidx.lifecycle.Transformations.map(viewModel.dataFromApi, input ->
input == null
);
}
}
ChildActivityViewModel.java
import android.os.Handler;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import com.maproductions.mohamedalaa.stackoverflow_solutions.models.DataFromApi;
import com.maproductions.mohamedalaa.stackoverflow_solutions.models.FakeDataFromApi;
/**
* Created by <a href="https://github.com/MohamedAlaaEldin636">Mohamed</a> on 6/4/2020.
*/
public class ChildActivityViewModel extends ViewModel {
public MutableLiveData<DataFromApi> dataFromApi = new MutableLiveData<>();
public void startLoadingDataFromApi() {
// Mock the api loading time
try {
new Handler().postDelayed(() -> {
// Do your magic here then change only your data value and isLoading will be auto changed,
// thanks to Transformations.map()
dataFromApi.setValue(FakeDataFromApi.get());
}, 5_000);
}catch (Throwable throwable) {
// Do nothing.
}
}
}
这篇关于在有api调用时如何在没有代码重复的情况下增加progressBar布局的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!