Path: blob/v3_openjdk/app_pojavlauncher/src/main/java/com/kdt/SideDialogView.java
2129 views
package com.kdt;12import static net.kdt.pojavlaunch.Tools.currentDisplayMetrics;34import android.animation.Animator;5import android.animation.AnimatorListenerAdapter;6import android.animation.ObjectAnimator;7import android.content.Context;8import android.util.Log;9import android.view.LayoutInflater;10import android.view.View;11import android.view.ViewGroup;12import android.view.animation.AccelerateDecelerateInterpolator;13import android.widget.Button;14import android.widget.TextView;1516import androidx.annotation.CallSuper;17import androidx.annotation.LayoutRes;18import androidx.annotation.NonNull;19import androidx.annotation.Nullable;20import androidx.annotation.StringRes;21import androidx.core.content.res.ResourcesCompat;2223import net.kdt.pojavlaunch.R;24import net.kdt.pojavlaunch.Tools;2526/**27* The base class for side dialog views28* A side dialog is a dialog appearing from one side of the screen29*/30public abstract class SideDialogView {3132private final ViewGroup mParent;33private final @LayoutRes int mLayoutId;34private ViewGroup mDialogLayout;35private DefocusableScrollView mScrollView;36protected View mDialogContent;3738protected final int mMargin;39private ObjectAnimator mSideDialogAnimator;40protected boolean mDisplaying = false;41/* Whether the layout is built */42private boolean mIsInstantiated = false;4344/* UI elements */45private Button mStartButton, mEndButton;46private TextView mTitleTextview;47private View mTitleDivider;4849/* Data to store when the UI element has yet to be inflated */50private @StringRes int mStartButtonStringId, mEndButtonStringId, mTitleStringId;51private View.OnClickListener mStartButtonListener, mEndButtonListener;525354public SideDialogView(Context context, ViewGroup parent, @LayoutRes int layoutId) {55mMargin = context.getResources().getDimensionPixelOffset(R.dimen._20sdp);56mParent = parent;57mLayoutId = layoutId;58}5960public void setTitle(@StringRes int textId) {61mTitleStringId = textId;62if (mIsInstantiated) {63mTitleTextview.setText(textId);64mTitleTextview.setVisibility(View.VISIBLE);65mTitleDivider.setVisibility(View.VISIBLE);66}67}6869public final void setStartButtonListener(@StringRes int textId, @Nullable View.OnClickListener listener) {70mStartButtonStringId = textId;71mStartButtonListener = listener;72if (mIsInstantiated) setButton(mStartButton, textId, listener);73}7475public final void setEndButtonListener(@StringRes int textId, @Nullable View.OnClickListener listener) {76mEndButtonStringId = textId;77mEndButtonListener = listener;78if (mIsInstantiated) setButton(mEndButton, textId, listener);79}8081private void setButton(@NonNull Button button, @StringRes int textId, @Nullable View.OnClickListener listener) {82button.setText(textId);83button.setOnClickListener(listener);84button.setVisibility(View.VISIBLE);85}868788private void inflateLayout() {89if(mIsInstantiated) {90Log.w("SideDialogView", "Layout already inflated");91return;92}9394// Inflate layouts95mDialogLayout = (ViewGroup) LayoutInflater.from(mParent.getContext()).inflate(R.layout.dialog_side_dialog, mParent, false);96mScrollView = mDialogLayout.findViewById(R.id.side_dialog_scrollview);97mStartButton = mDialogLayout.findViewById(R.id.side_dialog_start_button);98mEndButton = mDialogLayout.findViewById(R.id.side_dialog_end_button);99mTitleTextview = mDialogLayout.findViewById(R.id.side_dialog_title_textview);100mTitleDivider = mDialogLayout.findViewById(R.id.side_dialog_title_divider);101102LayoutInflater.from(mParent.getContext()).inflate(mLayoutId, mScrollView, true);103mDialogContent = mScrollView.getChildAt(0);104105// Attach layouts106mParent.addView(mDialogLayout);107108mSideDialogAnimator = ObjectAnimator.ofFloat(mDialogLayout, "x", 0).setDuration(600);109mSideDialogAnimator.setInterpolator(new AccelerateDecelerateInterpolator());110111mDialogLayout.setElevation(10);112mDialogLayout.setTranslationZ(10);113114mDialogLayout.setVisibility(View.VISIBLE);115mDialogLayout.setBackground(ResourcesCompat.getDrawable(mDialogLayout.getResources(), R.drawable.background_control_editor, null));116117//TODO offset better according to view width118mDialogLayout.setX(-mDialogLayout.getResources().getDimensionPixelOffset(R.dimen._280sdp));119mIsInstantiated = true;120121// Set up UI elements122if (mTitleStringId != 0) setTitle(mTitleStringId);123if (mStartButtonStringId != 0) setStartButtonListener(mStartButtonStringId, mStartButtonListener);124if (mEndButtonStringId != 0) setEndButtonListener(mEndButtonStringId, mEndButtonListener);125}126127/** Destroy the layout, cleanup variables */128private void deflateLayout() {129if(!mIsInstantiated) {130Log.w("SideDialogView", "Layout not inflated");131return;132}133134mSideDialogAnimator.removeAllUpdateListeners();135mSideDialogAnimator.removeAllListeners();136137mParent.removeView(mDialogLayout);138mIsInstantiated = false;139140mScrollView = null;141mSideDialogAnimator = null;142mDialogLayout = null;143mDialogContent = null;144mTitleTextview = null;145mTitleDivider = null;146mStartButton = null;147mEndButton = null;148}149150151/**152* Slide the layout into the visible screen area153*/154@CallSuper155public final void appear(boolean fromRight) {156if (!mIsInstantiated) {157inflateLayout();158onInflate();159}160161// To avoid UI sizing issue when the dialog is not fully inflated162onAppear();163Tools.runOnUiThread(() -> {164if (fromRight) {165if (!mDisplaying || !isAtRight()) {166mSideDialogAnimator.setFloatValues(currentDisplayMetrics.widthPixels, currentDisplayMetrics.widthPixels - mScrollView.getWidth() - mMargin);167mSideDialogAnimator.start();168mDisplaying = true;169}170} else {171if (!mDisplaying || isAtRight()) {172mSideDialogAnimator.setFloatValues(-mDialogLayout.getWidth(), mMargin);173mSideDialogAnimator.start();174mDisplaying = true;175}176}177});178}179180protected final boolean isAtRight() {181return mDialogLayout.getX() > currentDisplayMetrics.widthPixels / 2f;182}183184/**185* Slide out the layout186* @param destroy Whether the layout should be destroyed after disappearing.187* Recommended to be true if the layout is not going to be used anymore188*/189@CallSuper190public final void disappear(boolean destroy) {191if(!mIsInstantiated) {192Log.w("SideDialogView", "Layout not inflated");193return;194}195196if (!mDisplaying) {197if(destroy) {198onDisappear();199onDestroy();200deflateLayout();201}202return;203}204205mDisplaying = false;206if (isAtRight())207mSideDialogAnimator.setFloatValues(currentDisplayMetrics.widthPixels - mDialogLayout.getWidth() - mMargin, currentDisplayMetrics.widthPixels);208else209mSideDialogAnimator.setFloatValues(mMargin, -mDialogLayout.getWidth());210211if(destroy) {212onDisappear();213onDestroy();214mSideDialogAnimator.addListener(new AnimatorListenerAdapter() {215@Override216public void onAnimationEnd(Animator animation) {217deflateLayout();218}219});220}221222mSideDialogAnimator.start();223}224225/** @return Whether the dialog is currently displaying */226public final boolean isDisplaying(){227return mDisplaying;228}229230/**231* Called when the dialog is inflated, ideal for setting up UI elements bindings232*/233protected void onInflate() {}234235/**236* Called after the dialog has appeared237*/238protected void onAppear() {}239240/**241* Called after the dialog has disappeared242*/243protected void onDisappear() {}244245/**246* Called before the dialog gets destroyed (removing views from parent)247* Ideal for cleaning up resources248*/249protected void onDestroy() {}250251252}253254255