4

に があり、ListView別の から戻ったときにFragmentのデータを更新したいと考えています。のメソッドを上書きし、 のデータを変更してを呼び出しましたが、どういうわけかが更新されていません。に何か問題があると思われますが、エラーが見つからないようです。ListViewActivityonResume()FragmentAdapternotifyDataSetChanged()AdpaterListViewAdapter

これが私のコードですAdpater

class ManualExceptionsListAdapter extends BaseAdapter {

    private LayoutInflater mInflater;
    private TextView mManualExceptions;
    SwitchCompat mSwitch;
    TextView name;
    final Context context = getActivity();
    final SharedPreferences mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
    int a;
    int ifUse = 0;

    ManualExceptionsListAdapter(LayoutInflater inflater) {
        mInflater = inflater;
    }

    @Override
    public int getCount() {
        return (mPermanentManualException.size()+mContactsExceptionNumber.size());
    }

    @Override
    public Object getItem(int i) {
        return null;
    }

    @Override
    public long getItemId(int i) {
        return 0;
    }

    @Override
    public int getItemViewType(int position) {
        if (position < (mContactsExceptionNumber.size())) {
            a = 0;
            if(position == (mContactsExceptionNumber.size()-1)){
                ifUse = 1;
            }
            return a;
        } else {
            a = 1;
            return a;
        }
    }

    @Override
    public int getViewTypeCount() {
        return 2;
    }

    @Override
    public void notifyDataSetChanged() {
        super.notifyDataSetChanged();
    }

    @Override
    public View getView(int i, View view, ViewGroup viewGroup) {
        final int pos;
        if(mContactsExceptionNumber.size()>0) {
            pos = i - (mContactsExceptionNumber.size());
        }else{
            pos = 0;
        }
        int pos2 = 0;
        int type = getItemViewType(i);
        if(ifUse == 1){
            if(mContactsExceptionNumber.size()>0) {
                pos2 = i - (mContactsExceptionNumber.size());
                Exceptions.index = pos2;
            }
        }
        View v = view;
        if (view == null) {
            switch (type) {
                case 0:
                    v = mInflater.inflate(R.layout.contacts_exception_row, null);
                    name = (TextView) v.findViewById(R.id.contact_name);
                    name.setText(mContactsExceptionNames.get(i));
                    break;
                case 1:
                    v = mInflater.inflate(R.layout.manual_exception_row, null);
                    mManualExceptions = (TextView) v.findViewById(R.id.manual_exception_number);
                    mSwitch = (SwitchCompat) v.findViewById(R.id.manual_exception_switch);
                    mManualExceptions.setText(mPermanentManualException.get(pos2));
                    mSwitch.setTag(i);
                    try {
                        if (mManualExceptionList.contains(mPermanentManualException.get(pos2))) {
                            mSwitch.setChecked(true);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    break;
            }
        }else{
            switch (type) {
                case 0:
                    v = mInflater.inflate(R.layout.contacts_exception_row, null);
                    name = (TextView) v.findViewById(R.id.contact_name);
                    name.setText(mContactsExceptionNames.get(i));
                    break;
                case 1:
                    v = mInflater.inflate(R.layout.manual_exception_row, null);
                    mManualExceptions = (TextView) v.findViewById(R.id.manual_exception_number);
                    mSwitch = (SwitchCompat) v.findViewById(R.id.manual_exception_switch);
                    mManualExceptions.setText(mPermanentManualException.get(pos2));
                    mSwitch.setTag(i);
                    try {
                        if (mManualExceptionList.contains(mPermanentManualException.get(pos2))) {
                            mSwitch.setChecked(true);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    break;
            }
        }

        try {
            mSwitch.setOnTouchListener(new View.OnTouchListener() {
                @Override
                public boolean onTouch(View view, MotionEvent motionEvent) {
                    isTouched = true;
                    return false;
                }
            });
        } catch (NullPointerException e) {
            e.printStackTrace();
        }
        try {
            mSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
                @Override
                public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
                    if (isTouched) {
                        if (b) {
                            if (!mManualExceptionList.contains((mPermanentManualException.get(pos)))) {
                                mManualExceptionList.add((mPermanentManualException.get(pos)));
                            }
                            mSharedPreferences.edit().putString("ManualExceptions", TextUtils.
                                    join(",", mManualExceptionList)).apply();

                        } else {
                            try {
                                mManualExceptionList.remove((mPermanentManualException.get(pos)));
                                mSharedPreferences.edit().putString("ManualExceptions", TextUtils.
                                        join(",", mManualExceptionList)).apply();
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                        Log.d("RejectCall", "Permanent " + TextUtils.join(",", mPermanentManualException));
                        Log.d("RejectCall", TextUtils.join(",", mManualExceptionList));
                    }
                }
            });
        } catch (NullPointerException e) {
            e.printStackTrace();
        }

        return v;
    }

}
4

1 に答える 1

9

実装には複数の問題がありますAdapter。修正方法についてアドバイスするには多すぎます。Adapterを効率的に実装し、これを に適用する方法を説明するだけですAdapter

RecyclerView古いListView. _ RecyclerViewドキュメントはこちらで、Google ガイドの使用方法はこちらでご覧いただけます。


にデータを表示する場合ListViewは、最初に の各アイテムのレイアウトを作成する必要がありますListView。この例では、次のレイアウトを使用します。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="12dp">

    <CheckBox
        android:id="@+id/checkbox"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

</RelativeLayout>

の各項目に表示したいデータをListViewまとめるために、プライベート フィールドのデータだけを含む新しいクラスと、そのデータを取得および設定するゲッターとセッターを作成します。このようなクラスは通常、ビュー モデルと呼ばれます。上記のようなレイアウトのビュー モデルは次のようになります。

public class ExampleViewModel {

    // This is the text which will be set to the CheckBox
    private String text;

    // This boolean will be used to save the checked state of the CheckBox
    private boolean checked;

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

    public boolean isChecked() {
        return checked;
    }

    public void setChecked(boolean checked) {
        this.checked = checked;
    }
}

このようなビュー モデルの各インスタンスは、ListView. アイテムの 1 つが の可視領域に入ると、でListViewにバインドする必要があります(これは で実装する必要があるものです) 。アイテムが表示されている限り、モデルはこのモデルにバインドされたままになりますが、 が の表示領域を出るとすぐに、アイテムはリサイクルされ、表示領域に入る別のビュー モデルにバインドされます。これはビュー リサイクルと呼ばれ、のメモリ フットプリントを最小限に抑え、全体的なスクロール パフォーマンスと流動性を向上させるために行われます。非常に高価なオブジェクトであり、特に膨らませたり、ViewListViewgetView()AdapterViewViewListViewListViewViewsViewsfindViewById()多くのパフォーマンスを犠牲にし、リサイクルの主な観点は、Views一度だけ膨らませて再利用できるようにすることですfindViewById()

上で説明したことのほとんどは自動的に行われます。開発者としてあなたがしなければならないことは、適切なものViewsをインgetView()フレートするか、すでに利用可能なものがある場合はそれらを再利用してから、適切なビューモデルを にバインドすることViewです。最初に聞いた場合、これのほとんどはかなり複雑で混乱しているように見えますが、コードを見始めると、はるかに単純で明白になります。

これで、 のビュー アイテムのレイアウトと、ListViewそれに合わせたビュー モデルができました。ここで行う必要があるのは、通常はビュー ホルダーと呼ばれる別のクラスを作成することです。これらのビュー ホルダーは、基本的に、ListView. 各ビュー ホルダーにはView、 内の項目に関連付けられた 1 つが含まれてListViewおり、ビュー モデルのデータを にバインドする処理も行いますView。これ以上苦労することなく、上から見たビューモデルと一緒に行くビューホルダーがあります:

public class ExampleViewHolder {

    // The reference to the CheckBox is saved so we only have to perform the findViewById() once.
    private final CheckBox checkBox;

    // A reference to the view model which is currently bound to this view holder
    private ExampleViewModel currentModel;

    // The View associated with this view holder is passed into the constructor from the Adapter.
    public ExampleViewHolder(View view) {

        // And here we look for all relevant views
        // In our case we just need the CheckBox
        this.checkBox = (CheckBox) view.findViewById(R.id.checkbox);
    }

    public void bind(ExampleViewModel viewModel) {

        // Unset the listener in case there was one from a previous view model
        this.checkBox.setOnCheckedChangeListener(null);

        // Save a reference to the view model which is currently bound to this view holder
        this.currentModel = viewModel;

        // Bind the data to the CheckBox
        this.checkBox.setText(viewModel.getText());
        this.checkBox.setChecked(viewModel.isChecked());

        // Reset the listener
        this.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                currentModel.setChecked(isChecked);
            }
        });
    }
}

これでほぼ完了です。現在欠けている唯一のことは、これらすべてを次のように接続することAdapterです。

public class ExampleAdapter extends BaseAdapter {

    // Each type of view in the `ListView` gets its own id
    // In this example we only have one type of View so we only need one id
    private static final int EXAMPLE_VIEW_ID = 0;

    // The default view id is just a fallback
    private static final int DEFAULT_VIEW_ID = EXAMPLE_VIEW_ID;

    private final LayoutInflater inflater;
    private List<ExampleViewModel> viewModels;

    public ExampleAdapter(Context context, List<ExampleViewModel> viewModels) {

        // The view models are initially passed in through the constructor.
        // You can pass an empty list into the Adapter if there is not data initially.
        this.viewModels = viewModels;
        this.inflater = LayoutInflater.from(context);
    }

    @Override
    public int getCount() {
        if(viewModels == null) {
            return 0;
        }

        return viewModels.size();
    }

    @Override
    public Object getItem(int position) {
        return viewModels.get(position);
    }

    @Override
    public long getItemId(int position) {

        final Object model = getItem(position);

        // Here we check if the model is an instance of ExampleViewModel and if yes we return its id
        if(model instanceof ExampleViewModel) {
            return EXAMPLE_VIEW_ID;
        }

        return DEFAULT_VIEW_ID;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        if(getItemId(position) == EXAMPLE_VIEW_ID) {
            final ExampleViewModel model = (ExampleViewModel) getItem(position);

            final ExampleViewHolder viewHolder;

            // If the convertView is null we need to inflate a new view
            if(convertView == null) {
                final View view = this.inflater.inflate(ExampleViewHolder.LAYOUT, parent, false);   
                viewHolder = new ExampleViewHolder(view);

                // Here we set the viewHolder as tag to the View
                // This is done so we can reuse the same view holder later on
                // Essentially this is the integral part of the whole view recycling process
                view.setTag(viewHolder);
            } else {
                // If the convertView is not null we can just get the view holder with getTag() from the View
                viewHolder = (ExampleViewHolder) convertView.getTag();
            }

            // And we just need to bind the model to the view holder
            viewHolder.bind(model);
        }

        return convertView;
    }
}

そして、それはあなたが必要とするすべてです。これは、ほとんどのベスト プラクティスの実装ですAdapter。2 つ以上の異なるタイプのビューを扱っている場合は、タイプごとにビュー モデルとビュー ホルダー クラスを記述する必要があります。ViewModel次のようなインターフェイスを記述できます。

public interface ViewModel {

}

あなたのすべてのビュー モデルは、このインターフェイスを実装する必要があります。List<ViewModel>その後、Adapterすべての異なる種類のビュー モデルを含むことができるで使用できます。

public class TypeOneViewModel implements ViewModel {

}

public class TypeTwoViewModel implements ViewModel {

}

すべてのビュー モデルがこのインターフェイスを実装するとすぐに、これを行うことができます。

final List<ViewModel> models = new ArrayList<ViewModel>();
models.add(new TypeOneViewModel());
models.add(new TypeTwoViewModel());
...

そして、これListには複数の異なるタイプのビュー モデルが含まれるようになり、Adapter. は次のAdapterようになります。

public class ExampleAdapter extends BaseAdapter {

    private static final int TYPE_ONE_VIEW_ID = 0;
    private static final int TYPE_TWO_VIEW_ID = 1;
    private static final int DEFAULT_VIEW_ID = TYPE_ONE_VIEW_ID;

    private final LayoutInflater inflater;
    private List<ViewModel> viewModels;

    public ExampleAdapter(Context context, List<ViewModel> viewModels) {
        this.viewModels = viewModels;
        this.inflater = LayoutInflater.from(context);
    }

    @Override
    public int getCount() {
        if(viewModels == null) {
            return 0;
        }

        return viewModels.size();
    }

    @Override
    public ViewModel getItem(int position) {
        return viewModels.get(position);
    }

    @Override
    public long getItemId(int position) {

        final ViewModel model = getItem(position);

        if(model instanceof TypeOneViewModel) {
            return TYPE_ONE_VIEW_ID;
        }

        if(model instanceof TypeTwoViewModel) {
            return TYPE_TWO_VIEW_ID;
        }

        return DEFAULT_VIEW_ID;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        if(getItemId(position) == TYPE_ONE_VIEW_ID) {
            final TypeOneViewModel model = (TypeOneViewModel) getItem(position);

            final TypeOneViewHolder viewHolder;
            if(convertView == null) {
                final View view = this.inflater.inflate(TypeOneViewHolder.LAYOUT, parent, false);
                viewHolder = new TypeOneViewHolder(view);
                view.setTag(viewHolder);
            } else {
                viewHolder = (TypeOneViewHolder) convertView.getTag();
            }

            viewHolder.bind(model);
        }

        if(getItemId(position) == TYPE_TWO_VIEW_ID) {
            final TypeTwoViewModel model = (TypeTwoViewModel) getItem(position);

            final TypeTwoViewHolder viewHolder;
            if(convertView == null) {
                final View view = this.inflater.inflate(TypeTwoViewHolder.LAYOUT, parent, false);
                viewHolder = new TypeTwoViewHolder(view);
                view.setTag(viewHolder);
            } else {
                viewHolder = (TypeTwoViewHolder) convertView.getTag();
            }

            viewHolder.bind(model);
        }

        return convertView;
    }
}

抽象クラスを作成することで、ビュー ホルダーを統合することもできます。このような抽象クラスは次のようになります。

public abstract class ViewHolder<T extends ViewModel> {
    protected final View itemView;

    public ViewHolder(View view) {
        this.itemView = view;
    }

    public abstract void bind(T model);
}

この抽象クラスをビュー ホルダーの基本クラスとして使用する場合は、次のように記述します。

public class TypeOneViewHolder extends ViewHolder<TypeOneViewModel> {
    public TypeOneViewHolder(View view) {
        super(view);

        ...
    }

    public void bind(TypeOneViewModel model) {
        ...
    }
}

この部分は実際には必要ありませんが。で複数の異なるタイプのアイテムを処理する際の最も重要な部分ListViewは、すべてのモデルが共通のインターフェイスを実装しているため、それらを同じ に安全に配置できることですList

とにかく、この全体はあなたの よりもずっとシンプルできれいに見えますAdapterよね?このようにして、データを表示するListViewとのデータを完全に分離しViews、より保守しやすくなります。ビューのリサイクルを気にすることなく、ビュー ホルダーにアニメーションを簡単に実装でき、多くの要件の実装がはるかに簡単になります。もちろん、これらRecyclerViewすべてを次のレベルに引き上げます。ほぼ同じように機能しますが、 に比べていくつかの大きな改善点がありListViewます。ぜひご覧になることをお勧めします。


完全に忘れていたことの 1 つ:Listビュー モデルの内部をゲッターで公開できるので、ビュー モデルを外部から変更できます。次のようなメソッドを に追加しますAdapter

public List<ExampleViewModel> viewmodels() {
    return viewModels;
}

public void setViewModels(List<ExampleViewModel> models) {
    viewModels = models;
}

Adapter次に、次のようにビュー モデルを変更できます。

adapter.setViewModels(newData);
...
adapter.viewmodels().add(viewModel);

データの変更が終了したら、ListViewを呼び出しnotifyDataSetChanged()てを更新できますAdapter

于 2014-12-01T00:30:10.003 に答える