【问题标题】:Android dynamic fragment loading progress bar visibility won't setAndroid动态片段加载进度条可见性不会设置
【发布时间】:2016-12-14 13:38:26
【问题描述】:

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

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

布局方面,我有一个main_layout.xml,它有一个用于动态片段的框架布局和一个用于容纳进度条的静态相对布局。

目前,我的片段在元素的点击中从另一个内部加载和替换,但我已经删除了该代码。

问题

问题是LoadingFragment 中的setVisibilty 在从子类调用时似乎效果为零,例如MyFragment,这是为什么呢?

我已经给了LoadingFragment 它自己的View 变量viewMaster,我认为它应该引用main_layout,但可见性变化似乎仍然没有效果?

MainActivity

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();
        }
    }

}

ma​​in_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>

【问题讨论】:

  • 不要在 wrap_content 中使用 match_parent
  • "问题是从子类调用时,LoadingFragment 中的 setVisibilty 似乎效果为零,例如 MyFragment" 这是什么意思?您能否描述一个用例,包括用户采取的确切步骤、期望的行为以及实际结果?
  • 我也是为什么你在这里使用继承?您在不同片段之间共享哪些属性?
  • @Code-Apprentice 的想法是在上部主模板中有 1 个进度条,例如,当它们通过 api http 请求加载内容时,可以将其用作所有片段。这是我能想到的减少重复代码的唯一方法。
  • 另外,您要尽量减少重复的代码?我建议你做一些工作,即使它有重复的代码。您可以稍后重构重复项。这是一种常见的技术,尤其是在测试驱动开发中。即使您实际上并没有首先编写测试,您仍然可以使用重构步骤来获得很好的效果。

标签: android android-layout android-fragments


【解决方案1】:

问题是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。您可以在这里使用&lt;include&gt; 标签让生活更轻松。

  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 类和子类。

【讨论】:

  • 有没有我可以用顶部模板中的进度条来做到这一点?这种方式似乎更可重用并减少重复代码?
  • 这是一个很棒的实现,很棒的工作
【解决方案2】:

您通过 showLoader on View 调用 setVisibility,因为您将其他 View 作为片段视图返回。

类似的东西应该可以工作(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;
}

【讨论】:

  • 还是不隐藏加载器?
  • 在您的 xml 文件中将 fragment_holder 和 loading 都设置为不可见,并检查 showLoader 是否使加载可见
  • 也许你的异步任务被取消了?那么你也应该在 onCancelled 中添加 hideLoader
  • 如果我将android:visibility="invisible" 添加到@+id/loading 然后它永远不会出现在上面,它仍然不受影响。
  • 仔细检查代码,我已经运行了它,它按预期工作。为布局添加不同的背景,以便清楚地查看哪些添加了哪些没有添加
【解决方案3】:

如果您使用继承,则不能将布局分配给父片段。

您可以像这样使用黄油刀来利用视图绑定的继承,并编写很多干净的代码。

   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();
            }
        };

    }
} 

这里需要注意的是,你的片段布局必须有一个工具栏 ID htab_toolbar 和一个 ID 为 loading_view 的进度条,因为这是基类使用的

【讨论】:

    猜你喜欢
    • 2014-04-06
    • 2019-01-13
    • 2015-07-01
    • 1970-01-01
    • 2014-11-21
    • 2018-02-16
    • 1970-01-01
    • 2018-10-23
    • 1970-01-01
    相关资源
    最近更新 更多