我正在使用Android片段在我的应用程序中加载代码。要创建一个简单的加载器,我LoadingFragment extends Fragment
,然后我的片段类对其进行扩展,例如:MyFragment extends LoadingFragment
.
LoadingFragment
具有hideLoader
和showLoader
理论上我的子类 Fragment 应该能够调用onCreateView
和onPostExecute
,以在加载之间显示和隐藏进度条。
布局方面,我有一个main_layout.xml
,它有一个动态片段的框架布局和一个包含进度条的静态相对布局。
目前,我的片段在单击元素时从彼此内部加载和替换,但我已经删除了该代码。
问题所在
问题是LoadingFragment
中的setVisibilty
在从子类调用它时似乎没有影响,例如MyFragment
,这是为什么?
我已经给出了LoadingFragment
它自己的View
变量viewMaster
我认为应该参考main_layout
但可见性变化似乎仍然没有影响?
主活动
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyFragment myFragment = MyFragment.newInstance();
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_holder, myFragment).commit();
}
}
加载片段
public class LoadingFragment extends Fragment {
View viewMaster;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
viewMaster = inflater.inflate(R.layout.main_layout, container, false);
return viewMaster;
}
public void showLoader() {
viewMaster.findViewById(R.id.fragment_holder).setVisibility(View.INVISIBLE);
viewMaster.findViewById(R.id.loading).setVisibility(View.VISIBLE);
}
public void hideLoader() {
viewMaster.findViewById(R.id.fragment_holder).setVisibility(View.VISIBLE);
viewMaster.findViewById(R.id.loading).setVisibility(View.INVISIBLE);
}
}
我的片段
public class MyFragment extends LoadingFragment {
View view;
public MyFragment() {}
public static MyFragment newInstance() {
MyFragment fragment = new MyFragment();
return fragment;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
super.showLoader();
view = inflater.inflate(R.layout.fragment_lans, container, false);
MyFragment.ApiCallJob apicalljob = new MyFragment.ApiCallJob();
apicalljob.execute("a string");
return view;
}
public class ApiCallJob extends AsyncTask<String, Void, String> {
@Override
protected String doInBackground(String[] params) {
// do things
}
@Override
protected void onPostExecute(String data) {
// do things
// tried both to be sure but they should do the same?
hideLoader();
MyFragment.super.hideLoader();
}
}
}
main_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="app.stats.MainActivity"
android:orientation="horizontal">
<FrameLayout
android:id="@+id/fragment_holder"
android:layout_width="match_parent"
android:layout_height="match_parent">
</FrameLayout>
<RelativeLayout
android:id="@+id/loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ProgressBar
style="?android:attr/progressBarStyleLarge"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/progress_bar"/>
</RelativeLayout>
</RelativeLayout>
问题是
LoadingFragment
中的setVisibility
在从子类调用它时似乎没有影响,例如MyFragment
,这是为什么?
让我们看看调用MyFragment onCreateView
时会发生什么:
你打电话给super
onCreateView
这是在LoadingFragment
. 这会膨胀main_layout
并返回膨胀的视图。 请注意,此视图未在MyFragment
中引用(即View mainLayout = super.onCreateView(inflater, container, savedInstanceState);
)
您只是夸大了具有loading
视图组的视图,然后将其扔掉。但是,您确实设法在LoadingFragment
超类中保存了对该视图的引用。
接下来,您膨胀R.layout.fragment_lans
并(在开始加载后)将该视图作为要在片段中使用的视图返回。
因此,这里要观察的是,LoadingFragment
现在引用了片段的活动视图层次结构中没有的视图。
鉴于此,难怪setVisibility
不执行任何操作,因为片段没有显示该视图。
我不会使用继承来执行此操作,但如果您必须使用它,以下是解决问题的方法。
-
让我们只使用没有视图组包装器的
ProgressBar
。既然在RelativeLayout
,让我们把它居中。 然后让它android:visibility="gone"
,以便它有效地脱离片段布局:<ProgressBar android:id="@+id/progress_bar" style="?android:attr/progressBarStyleLarge" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:visibility="gone"/>
您将在每个
LoadingFragment
子类(包括fragment_lans.xml
)的布局中放置此进度条。 您可以使用<include>
标签让这里的生活更轻松。 -
更改
LoadingFragment
,以便 a) 它不会干扰其子类布局的膨胀,以及 b) 它使用子类的活动视图层次结构中的进度条:public class LoadingFragment extends Fragment { public void showLoader() { if (getView() != null) { View progressBar = getView().findViewById(R.id.progress_bar); if (progressBar != null) { progressBar.setVisibility(View.VISIBLE); } } } public void hideLoader() { if (getView() != null) { View progressBar = getView().findViewById(R.id.progress_bar); if (progressBar != null) { progressBar.setVisibility(View.GONE); } } } }
现在你应该看到你期望的。
编辑
如果你真的想使用你开始的布局方案,这是我的建议:
您的活动正在膨胀main_layout
具有进度条,因此我会将一些责任交给该活动。
现在,您可以为片段编写一些代码,这些代码将在视图层次结构中拖网并查找进度条。 那不是我的偏好。
这是另一种方法:
-
创建接口
interface ProgressDisplay { public void showProgress(); public void hideProgress(); }
-
使活动实现它
public class MainActivity extends AppCompatActivity implements ProgressDisplay {
。
public void showProgress() { findViewById(R.id.loading).setVisibility(View.VISIBLE); } public void hideProgress() { findViewById(R.id.loading).setVisibility(View.GONE); }
-
然后将调用活动的方法添加到
LoadingFragment
:protected void showProgress() { if (getActivity() instanceof ProgressDisplay) { ((ProgressDisplay) getActivity()).showProgress(); } } protected void hideProgress() { if (getActivity() instanceof ProgressDisplay) { ((ProgressDisplay) getActivity()).hideProgress(); } }
通过这种方式,您可以采用任何使用进度视图膨胀布局的活动,并让它实现ProgressDisplay
以便您可以将LoadingFragment
类与它一起使用。
你也可以创建一个LoadingActivity
类和子类。
您通过视图上的 showLoader 调用 setVisibility,该视图不用于在屏幕上绘制,因为您将其他视图作为片段视图返回。
这样的东西应该有效(MyFragment):
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = super.onCreateView(inflater, container, savedInstanceState);
ViewGroup vg = (ViewGroup) v.findViewById(R.id.fragment_holder)
inflater.inflate(R.layout.fragment_lans, vg, true);
MyFragment.ApiCallJob apicalljob = new MyFragment.ApiCallJob();
showLoader();
apicalljob.execute("a string");
return v;
}
如果使用继承,则无法将布局分配给父片段。
你可以像这样用黄油刀利用视图绑定的继承,并编写许多干净的代码。
import android.graphics.Color;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.widget.Toolbar;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
import com.emerson.techdayapp.R;
import com.emerson.techdayapp.view.activity.MainActivity ;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;
/**
* Base fragment created to be extended by every fragment in this application. This class provides
* dependency injection configuration, ButterKnife Android library configuration and some methods
* common to every fragment.
*/
public abstract class BaseFragment extends Fragment {
private Unbinder unbinder;
@BindView(R.id.htab_toolbar)
Toolbar mToolbar;
@BindView(R.id.loading_view)
ProgressBar loadingView;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(getFragmentLayout(), container, false);
}
@Override
public void onDestroy() {
super.onDestroy();
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
//bind view
injectViews(view);
//set page title
setUpToolBar(getPageTitle());
//add click listeners
setUpViewEvents();
//make service call
fillData();
}
/**
* @return resource if of fragment layout
*/
protected abstract int getFragmentLayout();
/**
* Set toolbar title
*
* @return
*/
protected abstract String getPageTitle();
/**
* Add click listeners
*/
protected abstract void setUpViewEvents();
/**
* Make Web serve calls here
*/
protected abstract void fillData();
/**
* Inject View
*
* @param view
*/
private void injectViews(final View view) {
unbinder = ButterKnife.bind(this, view);
}
/**
* Set up toolbar
*
* @param pageTitle
*/
private void setUpToolBar(String pageTitle) {
getActivity().setTitle(pageTitle);
if (mToolbar != null) {
((MainActivity ) getActivity()).setSupportActionBar(mToolbar);
((MainActivity ) getActivity()).getSupportActionBar()
.setDisplayHomeAsUpEnabled(true);
mToolbar.setNavigationIcon(R.drawable.ic_drawer);
mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//TODO what to do on back press
}
});
mToolbar.setTitleTextColor(Color.WHITE);
}
}
public void showProgressDialog() {
if (null != loadingView)
loadingView.setVisibility(View.VISIBLE);
}
public void dismissProgressDialog() {
if (null != loadingView)
loadingView.setVisibility(View.GONE);
}
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
}
然后像这样使用基础片段
public class MyFragment extends BaseFragment {
public MyFragment() {
}
public static MyFragment newInstance() {
MyFragment fragment = new MyFragment();
return fragment;
}
@Override
protected int getFragmentLayout() {
//put your frag layout id here
return R.layout.frag_pulse;
}
@Override
protected String getPageTitle() {
//Add some title to page
return "Some Title";
}
@Override
protected void setUpViewEvents() {
}
@Override
protected void fillData() {
new AsyncTask<String, Void, String>() {
@Override
protected void onPreExecute() {
super.onPreExecute();
//Show Progress
showProgressDialog();
}
@Override
protected String doInBackground(String... strings) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
//Hide Progress
dismissProgressDialog();
}
};
}
}
这里需要注意的是,您的片段布局必须有一个工具栏 IDhtab_toolbar
和一个带有 idloading_view
的进度条,因为这是由基类使用的