Code Xiaosheng, a technology platform focused on the Android field

Join my Android technology group

Author: AnonyPer links: https://www.jianshu.com/p/36e25602c5dd disclaimer: this article has been published, it has been approved AnonyPer forwarding etc. Please contact the author

preface

No matter what frame structure is used in Android project, the interface must be loaded with activities or fragments. For a user interface, some business logic processing is common, such as loading box when requesting network, and corresponding prompt when network error is required on the interface. For example, the general navigation bar, for example, every interface using activity needs to be configured in the manifest.xml file, etc. Can we do some encapsulation, so that developers can only focus on the specific logic of a specific interface, and quickly implement an interface?

Think about the direction

Based on the above questions, the content we want to encapsulate needs to meet the following requirements:

  • You don’t have to configure every user interface in the manifest.xml file

  • Universal navigation bar handling

  • Through simple inheritance, loading (which will be associated with network requests later) and abnormal interface (no data, no network, etc.) can be automatically implemented

Host selection for the user interface

If you don’t want to configure a lot of user interfaces in manifest.xml, using fragments is a natural choice. There are two ways to use fragments. One is that all fragments have a common Activity to host. Each time the user switches the interface, it still switches the Activity. The second one is to start only one Activity and switch the Fragment in the Activity to achieve the interface switch. The second option seems more reasonable, but the management of the interface life cycle and some common parameters can be confused if they are not used properly, so we adopt the first option.

Solution ideas

Define an interface that encapsulates the basic interface operations (loading, toast, displaying error messages, etc.) and then implement that interface method with a BaseFragment. An Activity is used to host the Fragment that implements the specific business of the BaseFragment. The parameter passed tells the Activity the name of the Fragment to be loaded. You only need to register a host Acitivty to display different user interfaces. The diagram below:

The process of soul painting by hand

The specific implementation

Follow the above ideas to define a user interface basic methods

Iview.java will also be used in MVP mode

package com.kotlin.anonyper.testapplication.base; /** * TestApplication * Created by anonyper on 2018/12/17. */public interface IView {/** * popup notification ** @param message */ void showToast(String message); /** * hideLoading bar */ void hideLoading(); /** * display loading ** @param message loading content * @param cancelAble whether to cancel */ void showLoading(String message, boolean cancelAble); Void showContentView(); void showContentView(); Void showExcptionView(int imageRes,); /** @param imageRes; /* @param message; String message); }Copy the code

Then use fragments to implement the IView interface and implement the methods in it

/** * TestApplication * Created by anonyper on 2018/12/18. */public Abstract Class BaseFragment extends Fragment implements IView { private ProgressDialog progressDialog; @Override public void showToast(String message) { Toast.makeText(this.getContext(), message, Toast.LENGTH_LONG).show(); } @Override public void hideLoading() { if (progressDialog ! = null && progressDialog.isShowing()) { progressDialog.dismiss(); } } @Override public void showLoading(String message, boolean cancelAble) { if (TextUtils.isEmpty(message)) { message = ""; } if (progressDialog == null) { progressDialog = new ProgressDialog(this.getActivity()); } if (this.getActivity().isFinishing()) { return; } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { if (this.getActivity().isDestroyed()) { return; } } progressDialog.setMessage(message); progressDialog.setCanceledOnTouchOutside(true); progressDialog.setCancelable(cancelAble); if (! progressDialog.isShowing()) { progressDialog.show(); } } @Override public void showContentView() { } @Override public void showExcptionView(int imageRes,String message) { }}Copy the code

ShowContentView and showExcptionView are not implemented. This will be implemented in a BaseFragment subclass.

For most user interfaces (with network requests), there is a similar style of title navigation, exception interface, and data interface switching display logic. In this case, we’ll wrap a SimpleBaseFragmen, store the display content and exception interface side by side with the FrameLayout container, and then arrange them vertically with the navigation view via the LinearLayout container. Let’s start with the public title class:

/** * public class TitleBar extends RelativeLayout {Context mContext; View titleView; @BindView(R.id.imgv_titleleft) ImageView imgvTitleleft; @BindView(R.id.rlt_titleleft) RelativeLayout rltTitleLeft; @BindView(R.id.tv_title) TextView tvTitle; @BindView(R.id.imgv_titleright) ImageView imgvTitleright; @BindView(R.id.tv_titleright) TextView tvTitleright; @BindView(R.id.rlt_titleright) RelativeLayout rltTitleright; @BindView(R.id.tv_titleline) View tvTitleline; @BindView(R.id.rlt_title) RelativeLayout rltTitle; public TitleBar(Context context) { super(context); this.mContext = context; initView(); } public TitleBar(Context context, AttributeSet attrs) { super(context, attrs); this.mContext = context; initView(); } public TitleBar(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); this.mContext = context; initView(); } private void initView() { titleView = View.inflate(getContext(), R.layout.view_title_bar, this); ButterKnife.bind(this, titleView); } public View getTitleView() { return titleView; } public void setTitle(String title) { if (tvTitle ! = null) { tvTitle.setText(title); } } public void setTitle(int res) { if (res > 0 && mContext ! = null) setTitle(mContext.getResources().getString(res)); } public View getLeftView() { return rltTitleLeft; } public View getRightView() { return rltTitleright; } public void setLeftImage(int res) { if (imgvTitleleft ! = null && res > 0) { imgvTitleleft.setImageResource(res); } } public void setRightImage(int res) { if (imgvTitleright ! = null && res > 0) { imgvTitleright.setImageResource(res); } } public void setRightText(String rightText) { if (tvTitleright ! = null) { tvTitleright.setText(rightText); } } public void setRightText(int rightText) { if (tvTitleright ! = null && rightText > 0) { tvTitleright.setText(rightText); } } public void hideTitle() { if (titleView ! = null) { titleView.setVisibility(View.GONE); } } public void showTitle() { if (titleView ! = null) { titleView.setVisibility(View.VISIBLE); } } public void hideLeftView() { if (rltTitleLeft ! = null) { rltTitleLeft.setVisibility(View.INVISIBLE); } } public void showLeftView() { if (rltTitleLeft ! = null) { rltTitleLeft.setVisibility(View.VISIBLE); } } public void hideLeftImage() { if (imgvTitleleft ! = null) { imgvTitleleft.setVisibility(View.GONE); rltTitleLeft.setVisibility(View.INVISIBLE); } } public void showLeftImage() { if (imgvTitleleft ! = null) { imgvTitleleft.setVisibility(View.VISIBLE); rltTitleLeft.setVisibility(View.VISIBLE); } } public void showRightText() { if (tvTitleright ! = null) { tvTitleright.setVisibility(View.VISIBLE); rltTitleright.setVisibility(View.VISIBLE); } } public void hideRightText() { if (tvTitleright ! = null) { tvTitleright.setVisibility(View.GONE); } } public void showRightImage() { if (imgvTitleright ! = null) { imgvTitleright.setVisibility(View.VISIBLE); rltTitleright.setVisibility(View.VISIBLE); } } public void hideRightImage() { if (imgvTitleright ! = null) { imgvTitleright.setVisibility(View.GONE); } } public void hideRightView() { if (rltTitleright ! = null) { rltTitleright.setVisibility(View.INVISIBLE); } } public void showRightView() { if (rltTitleright ! = null) { rltTitleright.setVisibility(View.VISIBLE); } } public void hideTitleLine() { if (tvTitleline ! = null) { tvTitleline.setVisibility(View.GONE); } } public void showTitleLine() { if (tvTitleline ! = null) { tvTitleline.setVisibility(View.VISIBLE); } } public void setRightClickListener(View.OnClickListener listener) { if (listener ! = null) { getRightView().setOnClickListener(listener); }}}Copy the code

Create a Primitive BaseFragment class that inherits the BaseFragment class and implements the control logic for the title, content view (provided by the fragment for the specific business), and exception view.

  • OnAttach is used to return the context content

@Override    public void onAttach(Context context) {        super.onAttach(context);        this.context = context;    }Copy the code
  • GetExcptionView gets the exception view. You can override this method if the exception view is not suitable for use

public View getExcptionView() { View excptionView = View.inflate(this.getContext(), R.layout.view_excption_empty, null);  excptionView.setVisibility(View.GONE); return excptionView; }Copy the code
  • InitTitle handles the title and adds the upper-left corner return

public void initTitle() {        if (mTitleBar != null) {            mTitleBar.showLeftView();            mTitleBar.getLeftView().setOnClickListener(v -> {                try {                    hideSoftInput();                } catch (Exception e) {                    e.printStackTrace();                }                getActivity().finish();            });        }    }Copy the code
  • Controls the display of content views and exceptions

@Override public void showContentView() { if (mainContentView ! = null) { mainContentView.setVisibility(View.VISIBLE); } if (excptionView ! = null) { excptionView.setVisibility(View.GONE); } } @Override public void showExcptionView(int imageRes, String message) { if (mainContentView ! = null) { mainContentView.setVisibility(View.GONE); } if (excptionView ! = null) { excptionView.setVisibility(View.VISIBLE); if (imageRes > 0) { ImageView image = excptionView.findViewById(R.id.error_imageview); if (image ! = null) { image.setImageResource(imageRes); } } if (! TextUtils.isEmpty(message)) { TextView descView = excptionView.findViewById(R.id.desc); if (descView ! = null) { descView.setText(message); } } excptionView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { onExcptionViewClick(); }}); }}Copy the code
  • Implementation of onCreateView method (emphasis)

In this method, we control the loading of Title View, business content View, and exception View:

public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { super.onCreateView(inflater, container, savedInstanceState); int viewId = getLayoutId(); if (viewId > 0) { contentView = inflater.inflate(viewId, null, false); // if (contentView ! = null) unbinder = ButterKnife.bind(this, contentView); } mTitleBar = new TitleBar(context); // Title bar mRootView = new LinearLayout(context); / / root view contains the title and a containing other contents view mRootView. SetBackgroundColor (getResources (). The getColor (R.c olor. App_bg_theme)); mRootView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); mRootView.setOrientation(LinearLayout.VERTICAL); mRootView.addView(mTitleBar); FrameLayout frameLayout = new FrameLayout(context); / / will be in addition to the title of the view in the frameLayout frameLayout. SetLayoutParams (new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); mainContentView = new LinearLayout(context); mainContentView.setLayoutParams(new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); mainContentView.setOrientation(LinearLayout.VERTICAL); if (contentView ! = null) { contentView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0, 1)); mainContentView.addView(contentView); } frameLayout.addView(mainContentView); excptionView = getExcptionView(); if (excptionView ! = null) { frameLayout.addView(excptionView); } mRootView.addView(frameLayout); // Set the Toolbar associated initTitle(); // Init controller initView(contentView, getArguments()); return mRootView; }/** @return display view layout resources */ public abstract int getLayoutId();Copy the code

Through the above method, we encapsulate a basic commonly used Fragment. In the process of concrete use, we implement public Abstract Int getLayoutId() by inheriting the SampleBaseFragment. Method, will automatically add the title, exception view and other logic.

Specific usage:

MainFragment.java

/** * TestApplication * Created by anonyper on 2018/12/18. */public class MainFragment extends SimpleBaseFragment { Override public int getLayoutId() {return r.layout.activity_main; } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); // This can handle business views or parameters. Views can be instantiated with a Butterknife. }}Copy the code

As can be seen, by inheriting SimpleBaseFragment, our MainFragment only needs to focus on specific business processing, and no longer needs to pay attention to the navigation bar, error interface and other logic.

Fragment encapsulation is implemented in the Fragment wrapper. Next, we will introduce the activities that host the Fragment. First, we will define a basic Activity, which is easy to write statistics, burying, etc code :BaseActivity

/** * Created by anonyper on 2018/12/18 TestApplication * Created by Anonyper on 2018/12/18 */public class BaseActivity extends AppCompatActivity { public String TAG = this.getClass().getName(); @Override protected void onResume() { super.onResume(); } @override protected void onDestroy() {super.ondestroy (); }}Copy the code

Then add a host for fragments by inheriting the basic Activity: SimpleBaseActivity.java

TestApplication * Created by Anonyper on 2018/12/18. */ Public class SimpleBaseActivity extends BaseActivity { BaseFragment baseFragment; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.view_sample_activity); if (getIntent().getStringExtra(BaseConfig.FRAGMENT_KEY) ! = null) { showFragment(); } } void showFragment() { Intent intent = getIntent(); if (intent == null) { return; } String targetFragment = intent.getStringExtra(BaseConfig.FRAGMENT_KEY); try { baseFragment = (BaseFragment) Class.forName(targetFragment).newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } if (baseFragment == null) { LogUtil.e(TAG, "targetFragment error"); return; } Bundle bundle = intent.getExtras(); if (bundle ! = null) { baseFragment.setArguments(bundle); } FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); ft.replace(R.id.fragment_container, baseFragment, this.getClass().getName()); ft.commitAllowingStateLoss(); } /** * The permission callback also notifies the fragment to prevent some fragments from obtaining privacy permissions * @param requestCode * @param permissions * @param grantResults */ @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (baseFragment ! = null) { baseFragment.onRequestPermissionsResult(requestCode, permissions, grantResults); }} /** * Fragment @param requestCode * @param resultCode * @param data */ @override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (baseFragment ! = null) { baseFragment.onActivityResult(requestCode, resultCode, data); }}}Copy the code

R.layout.view_sample_activity layout file is as follows:

<? The XML version = "1.0" encoding = "utf-8"? ><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/app_bg_theme" android:orientation="vertical"> <FrameLayout android:id="@+id/fragment_container" android:layout_width="fill_parent" android:layout_height="fill_parent"  /></LinearLayout>Copy the code

The manifast.xml configuration is as follows:

<activity            android:name="com.kotlin.anonyper.testapplication.activity.SimpleBaseActivity"            android:configChanges="orientation|screenSize|keyboardHidden"            android:label="@string/app_name">            <intent-filter>                <action android:name="com.kotlin.anonyper.testapplication.sample.action" />                <category android:name="android.intent.category.DEFAULT" />            </intent-filter>        </activity>Copy the code

Then use place, at the same time, the Intent to send com. The kotlin. Anonyper. Testapplication. Sample. The action of the action to start SimpleBaseActivity. Details are as follows:

Intent intent = new Intent();        intent.putExtra(BaseConfig.FRAGMENT_KEY, MainFragment.class.getName());        intent.setAction(BaseConfig.ACTION);        startActivity(intent);Copy the code

BaseConfig.FRAGMENT_KEY BaseConfig.ACTION BaseConfig.

public static final String FRAGMENT_KEY = "base_fragment"; public static final String ACTION = "com.kotlin.anonyper.testapplication.sample.action";Copy the code

The above code, completed the user interface View encapsulation, so that later in the process of writing code, only need to pay attention to the layout layout file and drink business logic, the rest will be completed automatically. Of course, it will be more meaningful to use this with the network data loading process. In the future, on this basis, and the use of network request process, let the two linkage!

The code download address: https://download.csdn.net/download/she_cool_/10861278

I’m serious about sharing technology