Попробую обрисовать ситуацию:
Есть Activity, в котором есть ListView.
В onCreate к ListView прикрепляется адаптер, основанный на SimpleCursorAdapter. Адаптер получает данные через CursorLoader и ContentProvider.
В onCreate, перед установкой адаптера я запускаю ProgressBar, который имеется в шаблоне с ListView
Код: Выделить всё
progressBar.setVisibility(View.VISIBLE)
Это несколько упрощенно, так как в действительности ProgressBar запускается с задержкой в 500 мс через handler.postDelaed. Если задержек в отображении ListView нет, то ProgressBar не мелькает. Но это не главное.
Вот скрины ListView для наглядности
На третьем скрине видно ProgressBar (в ActionBar). Вот с ним-то и проблемы.
Как видно, ListView имеет в каждом пункте RadioButton.
В БД есть список машин, и у каждой записи есть поле - is_primary, которое может быть 1 или 0.
Если значение поля 1, то RadioButton отмечен, если 0 - нет. Отмечен может быть только один авто.
Так вот, клик на пункте ListView - это два запроса в БД - два update, точнее один applyBatch.
У одной машины устанавливается is_primary в 0, у другой в 1.
ListView связан с курсором, а в ContentProvider курсор обновляется, поэтому все изменения немедленно отображаются на экране.
Обновление записей я изначально сделал в отдельном потоке. Но дальше захотел отображать ProgressBar, в случае если переключение RadioButton займет некоторое время. ProgressBar решил отображать в ActionBar - его-то и видно на 3 скрине.
Бился я с этим долго, вот тут есть тема http://4pda.ru/forum/index.php?showtopic=584019&st=20
В итоге я сейчас имею отдельный AsyncTaskLoader и класс его колбэков - все ради клика и двух Update в БД.
ProgressBar отображается корректно при поворотах девайса. Но некорректно в следующих случаях:
1. Если в момент транзакции нажать Home и тут же снова открыть приложение - ProgressBar отображается слишком долго, так как AsyncTaskLoader перезапускает loadInBackground
2. Если во время транзакции нажать Back и снова вернуться в Activity - ProgressBar вообще не появляется.
Сама транзакция (applyBatch) проходит и изменения видны, так как ListView связан с курсором. Но вот полностью синхронизировать ProgressBar с этой транзакцией не получается. Есть ли способ решить проблему?
Вот код на всякий случай
Код: Выделить всё
public class CarList extends ActionBarActivity implements LoaderCallbacks<Cursor>, OnSelectedDialog {
private Context context;
private ListView listView;
private ProgressBar progressBar;
private CarListSimpleCursorAdapter adapter;
private LoaderManager loaderManager;
private Loader<Integer> clickLoader;
private LoaderCallbacks<Integer> clickListener;
private static final int LOADER_CAR_LIST = 1;
private static final int LOADER_CLICK_LIST = 2;
protected static final int CAR_DELETE = 100;
protected static final int CAR_EDIT = 110;
protected static final int CAR_ADD = 120;
private static final String KEY_CAR_ID = "carId";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
supportRequestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
setContentView(R.layout.car_list_view);
android.support.v7.app.ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayHomeAsUpEnabled(true);
context = this;
listView = (ListView)findViewById(R.id.cars_list);
progressBar = (ProgressBar)findViewById(R.id.wait_dialog_progress_bar);
loaderManager = getSupportLoaderManager();
clickListener = new ClickCallbacks(this);
Button addNewCar = (Button)findViewById(R.id.button_add_car_list_cars);
addNewCar.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
startActivityForResult(new Intent(context, AddCar.class), CAR_ADD);
}
});
String[] from = {CarTable.KEY_BRAND};
int[] to = {R.id.car_list_item_brand_and_model};
adapter = new CarListSimpleCursorAdapter(context, R.layout.car_list_view_item, null, from, to, 0);
listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
listView.setAdapter(adapter);
showProgress();
loaderManager.initLoader(LOADER_CAR_LIST, null, this);
clickLoader = loaderManager.getLoader(LOADER_CLICK_LIST);
if(clickLoader != null && clickLoader.isStarted()){
Log.d(Consts.LOG, "onCreate: loader.isStarted");
loaderManager.initLoader(LOADER_CLICK_LIST, null, clickListener);
setSupportProgressBarIndeterminateVisibility(true);
}
listView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
RadioButton rb = (RadioButton)view.findViewById(R.id.car_list_radio_button_default_car);
if(rb.isChecked()){return;}
TextView tv = (TextView)view.findViewById(R.id.car_list_hidden_id_car);
int newPrimaryCarId = Integer.parseInt(tv.getText().toString());
Bundle args = new Bundle();
args.putInt(KEY_CAR_ID, newPrimaryCarId);
setSupportProgressBarIndeterminateVisibility(true);
if(clickLoader != null){
Log.d(Consts.LOG, "onClick: restartLoader");
loaderManager.restartLoader(LOADER_CLICK_LIST, args, clickListener);
}else{
Log.d(Consts.LOG, "onClick: initLoader");
clickLoader = loaderManager.initLoader(LOADER_CLICK_LIST, args, clickListener);
}
}
});
listView.setOnItemLongClickListener(new OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
TextView hiddenIdCar = (TextView)view.findViewById(R.id.car_list_hidden_id_car);
TextView brandModelView = (TextView)view.findViewById(R.id.car_list_item_brand_and_model);
int idCar = Integer.parseInt(hiddenIdCar.getText().toString());
String brandModel = brandModelView.getText().toString();
Car car = new Car(idCar, "", brandModel, 0, 0, 0, 0, 0, 0);
DialogFragment dialog = CarListLongPressedDialog.newInstance(
CarListLongPressedDialog.LONG_PRESS_DIALOG, car);
dialog.show(getSupportFragmentManager(), "tag_dialog");
return true;
}
});
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
@Override
public void onDestroy(){
if(progressHandler != null){
progressHandler.removeCallbacksAndMessages(null);
}
super.onDestroy();
}
@Override
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
String[] projection = CarTable.getProjection();
String sortOrder = CarTable.KEY_ID;
CursorLoader cursorLoader = new CursorLoader(context, Uri.parse(DataProvider.CAR_URL), projection, null, null, sortOrder);
return cursorLoader;
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
hideProgress();
adapter.swapCursor(cursor);
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
adapter.swapCursor(null);
}
/*** <ProgressRing> ***/
private Handler progressHandler;
private Runnable progressRunnable;
private void showProgress(){
progressHandler = new Handler();
progressRunnable = new Runnable() {
@Override
public void run() {
AlphaAnimation anim = new AlphaAnimation(0F, 1F);
anim.setDuration(500);
progressBar.startAnimation(anim);
progressBar.setVisibility(View.VISIBLE);
}
};
progressHandler.postDelayed(progressRunnable, 500);
}
private void hideProgress(){
if(progressHandler != null){
progressHandler.removeCallbacks(progressRunnable);
}
progressBar.setVisibility(View.GONE);
}
/*** </ProgressRing> ***/
@Override
public void onSelectLongPressDialog(int typeAction, Car car){
switch(typeAction){
case CAR_EDIT:
editCar(car);
break;
case CAR_DELETE:
DialogFragment dialog = CarListLongPressedDialog.newInstance(
CarListLongPressedDialog.DELETE_DIALOG, car);
dialog.show(getSupportFragmentManager(), "deleteCar");
break;
}
}
private void editCar(Car car){
Intent intent = new Intent(this, EditCar.class);
intent.putExtra(EditCar.CAR_OBJECT, car);
startActivityForResult(intent, CAR_EDIT);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch(requestCode){
case CAR_EDIT:
if(resultCode == RESULT_OK && loaderManager != null){
loaderManager.getLoader(LOADER_CAR_LIST).onContentChanged();
}else if(requestCode == Consts.RESULT_ERROR){
Toast.makeText(context, R.string.sss_system_error, Toast.LENGTH_LONG).show();
}
break;
case CAR_ADD:
if(resultCode == RESULT_OK && loaderManager != null){
loaderManager.getLoader(LOADER_CAR_LIST).onContentChanged();
}
break;
}
}
@Override
public void deleteCar(Car car) {
Uri uri = Uri.parse(DataProvider.CAR_URL +"/delete");
String selection = CarTable.KEY_ID +"=?";
String[] selectionArgs = {String.valueOf(car.id)};
DataAsyncQueryHandler asyncQueryHandler = new DataAsyncQueryHandler(
getContentResolver(), new DataAsyncQueryHandler.AsyncQueryListener() {
@Override
public void onUpdateComplete(int token, Object cookie, int result) {}
@Override
public void onQueryComplete(int token, Object cookie, Cursor cursor) {}
@Override
public void onInsertComplete(int token, Object cookie, Uri uri) {}
@Override
public void onDeleteComplete(int token, Object cookie, int result) {
if(loaderManager != null){
loaderManager.getLoader(LOADER_CAR_LIST).onContentChanged();
}
}
@Override
public void onApplyBranchComplete(ContentProviderResult[] result) {}
});
asyncQueryHandler.startDelete(1, null, uri, selection, selectionArgs);
}
/*** <Click Loader> ***/
public static class ClickLoader extends AsyncTaskLoader<Integer>{
private WeakReference<CarList> activity;
private int carId;
private ContentProviderResult[] result;
private boolean loading = false;
public ClickLoader(Context context, Bundle args) {
super(context);
this.carId = args.getInt(KEY_CAR_ID, -1);
this.activity = new WeakReference<CarList>((CarList)context);
}
@Override
public Integer loadInBackground() {
Log.d(Consts.LOG, "Loader: loadInBackground");
if(carId == -1){return -1;}
CarList activity = this.activity.get();
if(activity == null){return -1;}
final ArrayList<ContentProviderOperation> list = new ArrayList<ContentProviderOperation>();
list.add(
ContentProviderOperation.newUpdate(Uri.parse(DataProvider.CAR_URL +"/update")).
withValue(CarTable.KEY_PRIMARY_CAR, 0).
withSelection(CarTable.KEY_PRIMARY_CAR +"=?", new String[]{"1"}).build()
);
list.add(
ContentProviderOperation.newUpdate(Uri.parse(DataProvider.CAR_URL +"/update")).
withValue(CarTable.KEY_PRIMARY_CAR, 1).
withSelection(CarTable.KEY_ID +"=?", new String[]{String.valueOf(carId)}).build()
);
try {
result = activity.getContentResolver().applyBatch(DataProvider.AUTHORITY, list);
} catch (RemoteException | OperationApplicationException e) {
// TODO ERROR
loading = false;
return -1;
}
loading = false;
if(result.length == 2){
return 1;
}else{
return -1;
}
}
@Override
protected void onForceLoad() {
super.onForceLoad();
Log.d(Consts.LOG, "Loader: onForceLoad");
}
@Override
protected void onStartLoading() {
super.onStartLoading();
Log.d(Consts.LOG, "Loader: onStartLoading");
if(result == null && !loading){
loading = true;
forceLoad();
}else if(result != null){
deliverResult(1);
}
}
@Override
protected void onStopLoading() {
super.onStopLoading();
Log.d(Consts.LOG, "Loader: onStopLoading");
}
@Override
protected void onReset() {
super.onReset();
Log.d(Consts.LOG, "Loader: onReset");
}
}
public static class ClickCallbacks implements LoaderCallbacks<Integer>{
private WeakReference<CarList> activity;
public ClickCallbacks(CarList activity){
this.activity = new WeakReference<CarList>(activity);
}
@Override
public Loader<Integer> onCreateLoader(int arg0, Bundle args) {
Log.d(Consts.LOG, "Callbacks: onCreateLoader");
CarList activity = this.activity.get();
return new ClickLoader(activity, args);
}
@Override
public void onLoadFinished(Loader<Integer> loader, Integer result) {
Log.d(Consts.LOG, "Callbacks: onLoadFinished");
CarList activity = this.activity.get();
if(activity != null){
activity.setSupportProgressBarIndeterminateVisibility(false);
if(result == -1){
Toast.makeText(activity, R.string.sss_system_error, Toast.LENGTH_LONG).show();
}
}
}
@Override
public void onLoaderReset(Loader<Integer> arg0) {
Log.d(Consts.LOG, "Callbacks: onLoaderReset");
}
}
}