Использование setVisibility(View.INVISIBLE) с библиотекой Pre Lollipop Circular Reveal
Я работаю над проектом, в котором я хочу использовать эффект Circular Reveal в соответствии с Material Design. Проект имеет minSDK = 11, поэтому для совместимости с устройствами перед Lollipop я использую эту библиотеку https://github.com/ozodrukh/CircularReveal
У меня есть фрагмент с FloatingActionButton, который при касании преобразует себя в CardView, как описано здесь преобразований FAB.
Как только карта открыта, у нее есть кнопка, чтобы вернуть анимацию, преобразовав карту в FAB. Теперь моя проблема заключается в следующем: скажем, пользователь нажимает FAB, и открывается CardView. Теперь пользователь поворачивает свое устройство, поэтому активность сбрасывает фрагмент. Чего я хочу добиться, так это чтобы карта оставалась видимой и открытой, а FAB должен быть отключен и невидим. Проблема в том, что если я просто использую setVisibility(View.INVISIBLE)
на моем FAB это не работает (обратите внимание, что если я использую getVisibility()
сразу после установки его невидимым, он корректно возвращает мне значение 4 == View.INVISIBLE, но фаб все еще виден). Я должен обернуть setVisibility(...)
позвонить внутрь postDelayed()
с задержкой не менее 50-100 мс, чтобы сделать потрясающее изображение невидимым.
Итак, мой вопрос: я делаю все правильно или есть лучший способ выполнить то, что я хочу (потому что это кажется мне очень уродливым)?
Вот код Это мой XML-фрагмент:
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:id="@+id/my_appbar"
android:layout_width="match_parent"
android:layout_height="@dimen/toolbar_expanded_height"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.design.widget.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:expandedTitleMarginEnd="64dp"
app:expandedTitleMarginStart="70dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<android.support.v7.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:popupTheme="@style/ToolbarPopupTheme"
app:layout_collapseMode="pin"/>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
...
<io.codetail.widget.RevealFrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="@layout/my_fragment" />
</io.codetail.widget.RevealFrameLayout>
<android.support.design.widget.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/fab_margin"
app:fabSize="mini"
app:layout_anchor="@+id/my_appbar"
app:layout_anchorGravity="bottom|left|start" />
</android.support.design.widget.CoordinatorLayout>
Это макет XML включенного CardView:
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:layout_gravity="center"
card_view:cardCornerRadius="2dp"
android:visibility="invisible"
android:focusableInTouchMode="true">
<RelativeLayout android:layout_width="match_parent"
android:layout_height="match_parent">
...
<LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="end">
<Button android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="Ok" />
<Button android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Cancel" />
</LinearLayout>
</RelativeLayout>
</android.support.v7.widget.CardView>
И это код моего фрагмента:
public class MyFragment extends Fragment {
private static final String CARD_OPEN_TAG = "CARD_OPEN_TAG";
public static MyFragment newInstance(){
return new MyFragment();
}
private int cardOpen;
private FloatingActionButton fabAddPublication;
private CardView card;
private Button cardCancel;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final View rootView = inflater.inflate(R.layout.fragment_magazines, container, false);
toolbar = (Toolbar) rootView.findViewById(R.id.layout);
...
// Initialize view status
if (savedInstanceState != null){
cardOpen = savedInstanceState.getInt(CARD_OPEN_TAG);
} else {
cardOpen = -1;
}
...
// Get FAB reference
fab = (FloatingActionButton) rootView.findViewById(R.id.fab_id);
// Get card reference
card = (CardView) rootView.findViewById(R.id.card_id);
editorPublication.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
// Using this event because I need my card to be measured to move correctly fab at his center
if (cardOpen != -1){
// Move FAB to center of card
fab.setTranslationX(coordX); // WORKS
fab.setTranslationY(coordY); // WORKS
// fab.setVisibility(View.INVISIBLE) -> DOESN'T WORK, fab remain visible on top and at center of my card
// Ugly workaround
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
// Hide FAB
fab.setVisibility(View.INVISIBLE);
}
}, 50); // Sometimes fails: if device/emulator use too much time to "rotate" screen, fab stay visible
// Remove listener
ViewTreeObserver obs = card.getViewTreeObserver();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
obs.removeOnGlobalLayoutListener(this);
else obs.removeGlobalOnLayoutListener(this);
}
}
});
if (editorOpen != -1){
fab.setEnabled(false); // WORKS
card.setVisibility(View.VISIBLE); // WORKS
}
// Get editors buttons reference
cardCancel = (Button) card.findViewById(R.id.card_cancel_id);
// Set FAB listener
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Explode FAB
explodeFab(fab, card); // This method trigger the reveal animation
cardOpen = card.getId();
}
});
// Set editors button listeners
cardCancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Implode FAB
implodeFAB(fab, card); // This card reverts the reveal animation
cardOpen = -1;
}
});
...
return rootView;
}
@Override
public void onSaveInstanceState(Bundle outState){
super.onSaveInstanceState(outState);
...
outState.putInt(CARD_OPEN_TAG, cardOpen);
}
}
1 ответ
Я не думаю, что вы делаете правильно, и вот почему:
1.
Используемая вами библиотека циклического отображения несовместима с аппаратным ускорением в Android 11–17 (3,0–4,2). Он использует Canvas.clipPath() - метод, который был реализован в программном обеспечении только до Android 4.3. Это означает, что вам нужно отключить аппаратное ускорение, иначе ваше приложение будет зависать при неподдерживаемом вызове OpenGL.
Лучший способ обрезать макеты - использовать Canvas.saveLayer()/restoreLayer(). Это единственный способ поддержки всех устройств, получения сглаженного изображения и правильной поддержки аннулирования / разметки / отрисовки.
2.
Доверие таймеров и слушателей макетов к изменению макетов означает, что что-то действительно не работает для вас. Каждое изменение может быть выполнено напрямую, без ожидания. Вам просто нужно найти подходящее место для этого куска кода.
Таймеры также могут срабатывать, когда ваше приложение работает в фоновом режиме. Это означает, что произойдет сбой при попытке доступа к интерфейсу из потока таймера.
Может быть, вы вызываете setVisibility(true) где-то в коде, и поэтому ваш FAB виден? Настройка видимости в порядке и должна работать без задержек вызовов. Просто сохраните видимость как состояние FAB и восстановите ее после изменения ориентации.
Если вы хотите расположить FAB в центре панели инструментов, оберните их оба в FrameLayout (wrap_content) и расположите FAB с layout_gravity="center". Это должно позволить вам удалить макет слушателя.
3.
support.CardView не работает и не должен использоваться вообще. Это выглядит и работает немного по-другому на Lollipop и на старых системах. Тень другая, отступы разные, отсечение контента не работает на устройствах, предшествующих Lollipop, и т. Д. Вот почему трудно получить согласованные, хорошие результаты на всех платформах.
Вы должны рассмотреть возможность использования простых макетов для этой цели.
4.
Некоторые анимации выглядят круто, но сложны в реализации, не дают никакой ценности и быстро становятся раздражающими, потому что пользователь должен ждать завершения анимации каждый раз, когда нажимает кнопку.
Я не говорю "нет" для вашего случая, но вы можете удалить трансформацию и использовать выпадающее меню, нижний лист, статическую панель инструментов или диалог для этой цели.