У меня проблема с FAB, привязанным к списку (См. Пример видео), кажется, мерцает на ничью, изменяя якорь положение. Проблема возникает в эмуляторе, а также на любом устройстве, которое я тестировал на уровне API 19 после публикации.
У меня есть основное действие:
<LinearLayout
android:id="@+id/main_layout"
android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<!-- our toolbar -->
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:minHeight="?attr/actionBarSize"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
android:elevation="4dp"/>
<!-- our tablayout to display tabs -->
<android.support.design.widget.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:minHeight="?attr/actionBarSize"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
android:elevation="4dp"/>
<!-- View pager to swipe views -->
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="fill_parent" />
</LinearLayout>
В этом случае нижний nav
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#fff">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:id="@+id/svBody"
android:background="#fff">
</FrameLayout>
<android.support.design.widget.BottomNavigationView
android:id="@+id/bottomNavigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
app:itemBackground="@color/colorPrimary"
android:background="@color/colorPrimary"
app:itemIconTint="@color/cardview_light_background"
app:itemTextColor="@color/cardview_light_background"
app:menu="@menu/bottom_navigation_main">
</android.support.design.widget.BottomNavigationView>
</LinearLayout>
Что содержит фрагмент:
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/lvWords"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#fff">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal"
android:background="#fff"
android:elevation="4dp">
<TextView
android:text="Lipshapes"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:id="@+id/lipshapes_titlestrip"
android:background="#33b5e5"
android:textColor="#fff"
android:textSize="32sp"
android:paddingTop="4dp"
android:paddingBottom="4dp"
android:elevation="6dp"
android:paddingLeft="@dimen/margin_medium"
android:paddingRight="@dimen/margin_medium"/>
<TextView
android:id="@+id/words_titlestrip"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_gravity="bottom|right"
android:layout_weight="1"
android:background="#059FD8"
android:elevation="10dp"
android:paddingBottom="4dp"
android:paddingLeft="@dimen/margin_medium"
android:paddingRight="@dimen/margin_medium"
android:paddingTop="4dp"
android:text="Words"
android:textColor="#fff"
android:textSize="32sp" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="0dp"
android:orientation="vertical"
android:layout_weight="1"
android:elevation="0dp"
android:background="#fff">
<ListView
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
/>
<View
android:id="@+id/view"
android:layout_width="match_parent"
android:layout_height="4dp"
android:background="@drawable/shadow" />
</LinearLayout>
</LinearLayout>
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:layout_margin="16dp"
android:src="@drawable/plus_white_48"
app:layout_anchor="@android:id/list"
app:layout_anchorGravity="bottom|right|end"
app:fabSize="normal" />
И вот код фрагментов:
public static class WordListSectionFragment extends ListFragment implements AdapterView.OnItemClickListener, AdapterView.OnItemLongClickListener, View.OnClickListener, LoaderManager.LoaderCallbacks<Cursor> {
public SimpleCursorAdapter wordAdapter;
final static int LIST_VIEW = 0;
private FloatingActionButton fab;
String lipshape = null;
int lipshape_id = 0;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_section_wordlist, container, false);
fab = (FloatingActionButton) rootView.findViewById(R.id.fab);
Bundle args = getArguments();
lipshape = args.getString("lipshape_selected_desc");
lipshape_id = Integer.valueOf(args.getString("lipshape_selected_id"));
((TextView) rootView.findViewById(R.id.words_titlestrip)).setText(
lipshape
);
TextView lipshapesTV = (TextView) rootView.findViewById(R.id.lipshapes_titlestrip);
lipshapesTV.setOnClickListener(this);
return rootView;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// if you only want one column do it like this
// String[] projection = new String[]{BaseColumns._ID, VideoFilesContract.videoFiles.FILENAME};
wordAdapter =
new SimpleCursorAdapter(
getContext(),
R.layout.item_word,
null,
new String[] {WordContract.wordItems.DESCRIPTION, "number_of_videos"},
new int[] {R.id.tvWord, R.id.tvVideoCount},
LIST_VIEW);
// Setup cursor adapter using cursor from last step
setListAdapter(wordAdapter);
//getListView().setOnItemClickListener(this);
getLoaderManager().initLoader(LIST_VIEW, null, this);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
getListView().setOnItemClickListener(this);
getListView().setOnItemLongClickListener(this);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// Click action
//Toast.makeText(getActivity(), "Button Clicked!", Toast.LENGTH_SHORT).show();
Intent intent = new Intent(getActivity(), AddWord.class);
intent.putExtra("lipshape_selected_id", lipshape_id);
intent.putExtra("lipshape_selected_desc", lipshape);
startActivityForResult(intent, 1);
}
});
}
public void restartLoader()
{
wordAdapter.swapCursor(null);
getLoaderManager().restartLoader(LIST_VIEW, null, this);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch(requestCode) {
case (1) : {
if (resultCode == Activity.RESULT_OK) {
Boolean returnValue = data.getBooleanExtra("added_word", false);
if (returnValue)
{
restartLoader();
Snackbar.make(getView().getRootView().findViewById(R.id.lvWords), "Word Added Successfully!", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
}
break;
}
}
}
@Override
public void onClick(View v)
{
if (v.getId() == R.id.lipshapes_titlestrip)
{
LipshapeSectionFragment fragment = new LipshapeSectionFragment();
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction().replace(R.id.svBody, fragment).commit();
}
}
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position,long id)
{
Cursor _cursor = ((Cursor) getListView().getItemAtPosition(position));
Cursor cursor;
int count =0;
//do a count for videos if 0
int word_id = _cursor.getInt(_cursor.getColumnIndex("_id"));
String word = _cursor.getString(_cursor.getColumnIndex(WordContract.wordItems.DESCRIPTION));
ContentResolver resolver = getContext().getContentResolver();
if (word_id!=0) {
cursor =
resolver.query(VideoFilesContract.videoFiles.CONTENT_URI,
VideoFilesContract.videoFiles.PROJECTION_ALL,
"word_id=?",
new String[]{Integer.toString(word_id)},
null,
null);
count = cursor.getCount();
}
switch(count)
{
case 0:
Snackbar.make(view, "There are no videos recorded with this word.", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
break;
default:
Intent intent = new Intent(getActivity(), CollectionDemoActivity.class);
intent.putExtra("word_id", word_id);
intent.putExtra("word", word);
startActivity(intent);
}
//Toast.makeText(getActivity(), "Item: " + test, Toast.LENGTH_SHORT).show();
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle bundle) {
CursorLoader loader = null;
Bundle args = getArguments();
switch (id) {
case LIST_VIEW:
loader = new CursorLoader(
this.getActivity(),
WordContract.wordItems.CONTENT_TEST,
new String[]{WordContract.wordItems._ID, WordContract.wordItems.DESCRIPTION, "number_of_videos"},
null,
new String[]{args.getString("lipshape_selected_id")},
null);
return loader;
default:
return loader;
}
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
((SimpleCursorAdapter)this.getListAdapter()).
swapCursor(cursor);
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
((SimpleCursorAdapter)this.getListAdapter()).
swapCursor(null);
}
public void showDialog(final int word_id, String word_clicked) {
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle("Delete Word: "+ word_clicked + "?");
builder.setMessage("Deleting this word will also delete all videos tagged by this word.");
String positiveText = "Delete";
builder.setPositiveButton(positiveText,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
deleteWord(word_id);
//reload
}
});
String negativeText = getString(android.R.string.cancel);
builder.setNegativeButton(negativeText,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// negative button logic
}
});
AlertDialog dialog = builder.create();
// display dialog
dialog.show();
}
public boolean deleteWord(int word_id)
{
ContentResolver resolver = getContext().getContentResolver();
Uri delUri = ContentUris.withAppendedId(WordContract.wordItems.CONTENT_URI, word_id);
//resolver.delete(VideoFilesContract.videoFiles.CONTENT_URI, "_id=?", selectionArgs);
long resultCount = resolver.delete(delUri, null, null);
if (resultCount == 0)
{
//couldn't delete word with that id
return false;
}
else
{
restartLoader();
Snackbar.make(getListView(), "Word deleted successfully.", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
return true;
}
}
@Override
public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l)
{
//Snackbar.make(view, "Item has been long clicked!", Snackbar.LENGTH_LONG)
// .setAction("Action", null).show();
Cursor _cursor = ((Cursor) getListView().getItemAtPosition(i));
Cursor cursor;
int count =0;
//do a count for videos if 0
int word_id = _cursor.getInt(_cursor.getColumnIndex("_id"));
String word = _cursor.getString(_cursor.getColumnIndex(WordContract.wordItems.DESCRIPTION));
showDialog(word_id, word);
return true;
}
}