安卓动态片段加载进度条可见性无法设置



我正在使用Android片段在我的应用程序中加载代码。要创建一个简单的加载器,我LoadingFragment extends Fragment,然后我的片段类对其进行扩展,例如:MyFragment extends LoadingFragment.

LoadingFragment具有hideLoadershowLoader理论上我的子类 Fragment 应该能够调用onCreateViewonPostExecute,以在加载之间显示和隐藏进度条。

布局方面,我有一个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时会发生什么:

你打电话给superonCreateView这是在LoadingFragment. 这会膨胀main_layout并返回膨胀的视图。 请注意,此视图未在MyFragment中引用(即View mainLayout = super.onCreateView(inflater, container, savedInstanceState);)

您只是夸大了具有loading视图组的视图,然后将其扔掉。但是,您确实设法在LoadingFragment超类中保存了对该视图的引用。

接下来,您膨胀R.layout.fragment_lans并(在开始加载后)将该视图作为要在片段中使用的视图返回。

因此,这里要观察的是,LoadingFragment现在引用了片段的活动视图层次结构中没有的视图。

鉴于此,难怪setVisibility不执行任何操作,因为片段没有显示该视图。

我不会使用继承来执行此操作,但如果您必须使用它,以下是解决问题的方法。

  1. 让我们只使用没有视图组包装器的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>标签让这里的生活更轻松。

  2. 更改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的进度条,因为这是由基类使用的

最新更新