在有api调用时如何在没有代码重复的情况下增加progressBar布局 [英] How to inflate progressBar layout without code duplication when there is an api call

查看:172
本文介绍了在有api调用时如何在没有代码重复的情况下增加progressBar布局的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的应用程序中包含以下流程:

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.VisibleView.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重复.
  • 解决方案

    1. 按如下所示更改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屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆