Input:
import android.content.Intent;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.buaa.food.CommentPreview;
import com.buaa.food.DataBaseHelper;
import com.buaa.food.DishPreview;
import com.buaa.food.R;
import com.buaa.food.app.AppActivity;
import com.buaa.food.app.TitleBarFragment;
import com.buaa.food.ui.activity.CommentActivity;
import com.buaa.food.ui.activity.DishDetailActivity;
import com.buaa.food.ui.activity.admin.DishUploadActivity;
import com.buaa.food.ui.adapter.CommentsAdapter;
import com.buaa.food.ui.adapter.DishesAdapter;
import com.hjq.base.BaseAdapter;
import com.hjq.widget.layout.WrapRecyclerView;
import com.scwang.smart.refresh.layout.SmartRefreshLayout;
import com.scwang.smart.refresh.layout.api.RefreshLayout;
import com.scwang.smart.refresh.layout.listener.OnRefreshLoadMoreListener;
import java.util.ArrayList;
import java.util.List;
import timber.log.Timber;

Context:
app/src/main/java/com/buaa/food/ui/fragment/CommentsFragment.java
package com.buaa.food.ui.fragment;

public final class CommentsFragment extends TitleBarFragment<AppActivity>
        implements OnRefreshLoadMoreListener,
        BaseAdapter.OnItemClickListener {

    public enum StatusType {
        CommentOne,
        CommentTwo
    }

    private final int id;
    private final StatusType type;

    private List<CommentPreview> allComments;

    public static CommentsFragment newInstance(int id, StatusType type) {
        return new CommentsFragment(id, type);
    }

    public CommentsFragment(int id, StatusType type) {
        this.id = id;
        this.type = type;
    }

    private SmartRefreshLayout mRefreshLayout;
    private WrapRecyclerView mRecyclerView;

    private CommentsAdapter mAdapter;

    // 添加数据库连接
    private DataBaseHelper dataBaseHelper;

    @Override
    protected int getLayoutId() {
        return R.layout.status_fragment_comment;
    }

    @Override
    protected void initView() {
        mRefreshLayout = findViewById(R.id.rl_status_refresh_comment);
        mRecyclerView = findViewById(R.id.rv_status_list_comment);

        mAdapter = new CommentsAdapter(getAttachActivity());
        mAdapter.setOnItemClickListener(this);
        mRecyclerView.setAdapter(mAdapter);

        mRefreshLayout.setOnRefreshLoadMoreListener(this);

        this.dataBaseHelper = new DataBaseHelper(this.getContext());
    }

    @Override
    protected void initData() {
        mAdapter.setData(analogData());
    }

    private List<CommentPreview> analogData() {

app/src/main/java/com/buaa/food/CommentPreview.java
public class CommentPreview implements Comparable<CommentPreview> {
    int dishId;
    int commentId;
    String userName;
    String comment;
    String time;

    public CommentPreview(int dishId, int commentId, String userName, String comment, String time) {
        this.dishId = dishId;
        this.commentId = commentId;
        this.userName = userName;
        this.comment = comment;
        this.time = time;
    }

    public int getDishId() {
        return dishId;
    }

    public int getCommentId() {
        return commentId;
    }

    public String getUserName() {
        return userName;
    }

    public String getComment() {
        return comment;
    }

    public String getTime() {
        return time;
    }

    @Override
    public int compareTo(CommentPreview other) {
        return Integer.compare(this.commentId, other.commentId);
    }
}

app/src/main/java/com/buaa/food/ui/adapter/CommentsAdapter.java
public final class CommentsAdapter extends AppAdapter<CommentPreview> {

    public CommentsAdapter(Context context) {
        super(context);
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        return new ViewHolder();
    }

    private final class ViewHolder extends AppAdapter<?>.ViewHolder {

        private final TextView mCommentText;
        private final TextView mUserText;

        private ViewHolder() {
            super(R.layout.comment_item);
            mCommentText = findViewById(R.id.tv_comment_content);
            mUserText = findViewById(R.id.tv_comment_time);
        }

        @SuppressLint({"SetTextI18n", "UseCompatLoadingForDrawables"})
        @Override
        public void onBindView(int position) {
            mCommentText.setText(getItem(position).getComment());
            mUserText.setText(getItem(position).getUserName() + "  " + getItem(position).getTime());
        }
    }
}

app/src/main/java/com/buaa/food/ui/activity/DishDetailActivity.java
public final class DishDetailActivity extends AppActivity {

    private int dishId;
    boolean isCollected = false;

    private AppCompatImageView mDishImageView;
    private AppCompatTextView mDishNameView;
    private AppCompatTextView mDishPriceView;
    private AppCompatTextView mDishRemainingView;
    private AppCompatTextView mDishCanteenView;
    private AppCompatTextView mDishWindowView;
    private SettingBar mCommentEnterBar;
    private AppCompatButton mBuyBtn;
    private AppCompatImageView mCollectionView;

    private DataBaseHelper dataBaseHelper;

    @Override
    protected int getLayoutId() {
        return R.layout.dish_detail_activity;
    }

    @Override
    protected void initView() {
        dishId = getIntent().getIntExtra("dishId", 0);

        mDishImageView = findViewById(R.id.iv_dish_image);
        mDishNameView = findViewById(R.id.tv_dish_name);
        mDishPriceView = findViewById(R.id.tv_dish_price);
        mDishRemainingView = findViewById(R.id.tv_dish_remaining);
        mDishCanteenView = findViewById(R.id.tv_dish_canteen);
        mDishWindowView = findViewById(R.id.tv_dish_window);
        mCommentEnterBar = findViewById(R.id.sb_comment_enter);
        mBuyBtn = findViewById(R.id.btn_buy);
        mCollectionView = findViewById(R.id.iv_collection);

        setOnClickListener(mCommentEnterBar, mBuyBtn, mCollectionView, mCommentEnterBar);

        dataBaseHelper = new DataBaseHelper(this);
    }

    @SuppressLint("SetTextI18n")
    @Override
    protected void initData() {
        byte[] imgByte = dataBaseHelper.getDishImage(dishId);
        if (imgByte != null && imgByte.length > 0) {
            Bitmap imgBitmap = BitmapFactory.decodeByteArray(imgByte, 0, imgByte.length);

            GlideApp.with(getActivity())
                .load(imgBitmap)
                .placeholder(R.drawable.dish_1)
                .error(R.drawable.dish_1)
                .transform(new MultiTransformation<>(new CenterCrop()))
                .into(mDishImageView);
        } else {
            GlideApp.with(getActivity())
                .load(R.drawable.default1)
                .placeholder(R.drawable.dish_1)
                .error(R.drawable.dish_1)
                .transform(new MultiTransformation<>(new CenterCrop()))
                .into(mDishImageView);
        }

        mDishNameView.setText(dataBaseHelper.getDishName(dishId));
        mDishPriceView.setText(dataBaseHelper.getDishPrice(dishId) + " 元");
        mDishRemainingView.setText(dataBaseHelper.getDishRemaining(dishId) + " 份");
        mDishCanteenView.setText(dataBaseHelper.getDishCanteen(dishId));
        mDishWindowView.setText(dataBaseHelper.getDishWindow(dishId));

        // TODO : init mCollectionView ==> has done
        // isCollected = dataBaseHelper.isDishCollected(dishId);
        int curViewed = dataBaseHelper.getDishViewed(dishId);
        if (curViewed != -1) {
            dataBaseHelper.updateDishViewed(dishId, curViewed + 1);
        }

        isCollected = dataBaseHelper.isFavorite(dishId);

        if (isCollected) {
            mCollectionView.setImageResource(R.drawable.collected);
        } else {
            mCollectionView.setImageResource(R.drawable.collection_icon);
        }
    }

    @SingleClick
    @Override
    public void onClick(View view) {
        if (view == mBuyBtn) {
            if (dataBaseHelper.checkDishRemaining(dishId)) {
                int curRemain = dataBaseHelper.getDishRemaining(dishId);
                int curOrdered = dataBaseHelper.getDishOrdered(dishId);
                if (curOrdered != -1 && curRemain != -1) {
                    dataBaseHelper.updateDishRemaining(dishId, curRemain - 1);
                    dataBaseHelper.updateDishOrdered(dishId, curOrdered + 1);
                    Toast.makeText(DishDetailActivity.this, "菜品购买成功", Toast.LENGTH_SHORT).show();
                    dataBaseHelper.uploadHistory(dishId);
                } else {
                    Toast.makeText(DishDetailActivity.this, "菜品购买失败", Toast.LENGTH_SHORT).show();
                }
            } else {
                Toast.makeText(DishDetailActivity.this, "购买失败，菜品余量不足", Toast.LENGTH_SHORT).show();
            }

            // dataBaseHelper.updateDishViewed(dishId, dataBaseHelper.getDishViewed(dishId) + 1);

            // TODO : Add to user's history ==> has done
            finish();

        } else if (view == mCommentEnterBar) {
            Intent intent = new Intent(getActivity(), CommentActivity.class);
            intent.putExtra("dishId", dishId);
            intent.putExtra("commentId", 0);
            startActivity(intent);
        } else if (view == mCollectionView) {
            // dataBaseHelper.uploadFavorite(dishId);

            // TODO : Add to user's collection or cancel collection ==> has done

            if (isCollected) {
                dataBaseHelper.deleteCollection(dishId);
                toast("取消收藏");
                mCollectionView.setImageResource(R.drawable.collection_icon);
                isCollected = false;
            } else {
                dataBaseHelper.uploadFavorite(dishId);
                toast("收藏成功");
                mCollectionView.setImageResource(R.drawable.collected);
                isCollected = true;
            }

        }
    }
}

library/widget/src/main/java/com/hjq/widget/layout/WrapRecyclerView.java
@SuppressWarnings("rawtypes")
public final class WrapRecyclerView extends RecyclerView {

    /** 原有的适配器 */
    private RecyclerView.Adapter mRealAdapter;

    /** 支持添加头部和底部的适配器 */
    private final WrapRecyclerAdapter mWrapAdapter = new WrapRecyclerAdapter();

    public WrapRecyclerView(Context context) {
        super(context);
    }

    public WrapRecyclerView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public WrapRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public void setAdapter(Adapter adapter) {
        mRealAdapter = adapter;
        // 偷梁换柱
        mWrapAdapter.setRealAdapter(mRealAdapter);
        // 禁用条目动画
        setItemAnimator(null);
        super.setAdapter(mWrapAdapter);
    }

    @Override
    public Adapter getAdapter() {
        return mRealAdapter;
    }

    /**
     * 添加头部View
     */
    public void addHeaderView(View view) {
        mWrapAdapter.addHeaderView(view);
    }

    @SuppressWarnings("unchecked")
    public <V extends View> V addHeaderView(@LayoutRes int id) {
        View headerView = LayoutInflater.from(getContext()).inflate(id, this, false);
        addHeaderView(headerView);
        return (V) headerView;
    }

    /**
     * 移除头部View
     */
    public void removeHeaderView(View view) {
        mWrapAdapter.removeHeaderView(view);
    }

    /**
     * 添加底部View
     */
    public void addFooterView(View view) {
        mWrapAdapter.addFooterView(view);
    }

    @SuppressWarnings("unchecked")
    public <V extends View> V addFooterView(@LayoutRes int id) {
        View footerView = LayoutInflater.from(getContext()).inflate(id, this, false);
        addFooterView(footerView);
        return (V) footerView;
    }

    /**
     * 移除底部View
     */
    public void removeFooterView(View view) {
        mWrapAdapter.removeFooterView(view);
    }

    /**
     * 获取头部View总数
     */
    public int getHeaderViewsCount() {
        return mWrapAdapter.getHeaderViewsCount();
    }

    /**
     * 获取底部View总数
     */
    public int getFooterViewsCount() {
        return mWrapAdapter.getFooterViewsCount();
    }

    /**
     * 获取头部View集合
     */
    public List<View> getHeaderViews() {
        return mWrapAdapter.getHeaderViews();
    }

    /**
     * 获取底部View集合
     */
    public List<View> getFooterViews() {
        return mWrapAdapter.getFooterViews();
    }

    /**
     * 刷新头部和底部布局所有的 View 的状态
     */
    public void refreshHeaderFooterViews() {
        mWrapAdapter.notifyDataSetChanged();
    }

    /**
     * 设置在 GridLayoutManager 模式下头部和尾部都是独占一行的效果
     */
    public void adjustSpanSize() {
        final RecyclerView.LayoutManager layoutManager = getLayoutManager();
        if (!(layoutManager instanceof GridLayoutManager)) {
            return;
        }

        ((GridLayoutManager) layoutManager).setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {

            @Override
            public int getSpanSize(int position) {
                return (position < mWrapAdapter.getHeaderViewsCount()
                        || position >= mWrapAdapter.getHeaderViewsCount() + (mRealAdapter == null ? 0 : mRealAdapter.getItemCount()))
                        ? ((GridLayoutManager) layoutManager).getSpanCount() : 1;
            }
        });
    }

    /**
     * 采用装饰设计模式，将原有的适配器包装起来
     */
    private static final class WrapRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

        /** 头部条目类型 */
        private static final int HEADER_VIEW_TYPE = Integer.MIN_VALUE >> 1;
        /** 底部条目类型 */
        private static final int FOOTER_VIEW_TYPE = Integer.MAX_VALUE >> 1;

        /** 原有的适配器 */
        private RecyclerView.Adapter mRealAdapter;
        /** 头部View集合 */
        private final List<View> mHeaderViews = new ArrayList<>();
        /** 底部View集合 */
        private final List<View> mFooterViews = new ArrayList<>();
        /** 当前调用的位置 */
        private int mCurrentPosition;

        /** RecyclerView对象 */
        private RecyclerView mRecyclerView;

        /** 数据观察者对象 */
        private WrapAdapterDataObserver mObserver;

        private void setRealAdapter(RecyclerView.Adapter adapter) {
            if (mRealAdapter == adapter) {
                return;
            }

            if (mRealAdapter != null) {
                if (mObserver != null) {
                    // 为原有的RecyclerAdapter移除数据监听对象
                    mRealAdapter.unregisterAdapterDataObserver(mObserver);
                }
            }

            mRealAdapter = adapter;
            if (mRealAdapter == null) {
                return;
            }

            if (mObserver == null) {
                mObserver = new WrapAdapterDataObserver(this);
            }
            // 为原有的RecyclerAdapter添加数据监听对象
            mRealAdapter.registerAdapterDataObserver(mObserver);
            // 适配器不是第一次被绑定到RecyclerView上需要发送通知，因为第一次绑定会自动通知
            if (mRecyclerView != null) {
                notifyDataSetChanged();
            }
        }

        @Override
        public int getItemCount() {
            int itemCount = 0;
            if (mRealAdapter != null) {
                itemCount = mRealAdapter.getItemCount();
            }
            return getHeaderViewsCount() + itemCount + getFooterViewsCount();
        }

        @SuppressWarnings("all")
        @Override
        public int getItemViewType(int position) {
            mCurrentPosition = position;
            // 获取头部布局的总数
            int headerCount = getHeaderViewsCount();
            // 获取原有适配器的总数
            int adapterCount = mRealAdapter != null ? mRealAdapter.getItemCount() : 0;
            // 获取在原有适配器上的位置
            int adjPosition = position - headerCount;
            if (position < headerCount) {
                return HEADER_VIEW_TYPE;
            } else if (adjPosition < adapterCount) {
                return mRealAdapter.getItemViewType(adjPosition);
            }
            return FOOTER_VIEW_TYPE;
        }

        public int getPosition() {
            return mCurrentPosition;
        }

        @SuppressWarnings("all")
        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            switch (viewType) {
                case HEADER_VIEW_TYPE:
                    return newWrapViewHolder(mHeaderViews.get(getPosition()));
                case FOOTER_VIEW_TYPE:
                    return newWrapViewHolder(mFooterViews.get(getPosition() - getHeaderViewsCount() - (mRealAdapter != null ? mRealAdapter.getItemCount() : 0)));
                default:
                    int itemViewType = mRealAdapter.getItemViewType(getPosition() - getHeaderViewsCount());
                    if (itemViewType == HEADER_VIEW_TYPE || itemViewType == FOOTER_VIEW_TYPE) {
                        throw new IllegalStateException("Please do not use this type as itemType");
                    }
                    if (mRealAdapter == null) {
                        return null;
                    }
                    return mRealAdapter.onCreateViewHolder(parent, itemViewType);
            }
        }

        @SuppressWarnings("unchecked")
        @Override
        public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
            if (mRealAdapter == null) {
                return;
            }
            int viewType = getItemViewType(position);
            switch (viewType) {
                case HEADER_VIEW_TYPE:
                case FOOTER_VIEW_TYPE:
                    break;
                default:
                    mRealAdapter.onBindViewHolder(holder, getPosition() - getHeaderViewsCount());
                    break;
            }
        }

        private WrapViewHolder newWrapViewHolder(View view) {
            ViewParent parent = view.getParent();
            if (parent instanceof ViewGroup) {
                // IllegalStateException: ViewHolder views must not be attached when created.
                // Ensure that you are not passing 'true' to the attachToRoot parameter of LayoutInflater.inflate(..., boolean attachToRoot)
                ((ViewGroup) parent).removeView(view);
            }
            return new WrapViewHolder(view);
        }

        @Override
        public long getItemId(int position) {
            if (mRealAdapter != null && position > getHeaderViewsCount() - 1 && position < getHeaderViewsCount() + mRealAdapter.getItemCount()) {
                return mRealAdapter.getItemId(position - getHeaderViewsCount());
            }
            return super.getItemId(position);
        }

        @Override
        public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
            mRecyclerView = recyclerView;
            if (mRealAdapter == null) {
                return;
            }
            mRealAdapter.onAttachedToRecyclerView(recyclerView);
        }

        @Override
        public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {
            mRecyclerView = null;
            if (mRealAdapter == null) {
                return;
            }
            mRealAdapter.onDetachedFromRecyclerView(recyclerView);
        }

        @SuppressWarnings("unchecked")
        @Override
        public void onViewRecycled(@NonNull ViewHolder holder) {
            if (holder instanceof WrapViewHolder) {
                // 防止这个 ViewHolder 被 RecyclerView 拿去复用
                holder.setIsRecyclable(false);
                return;
            }
            if (mRealAdapter == null) {
                return;
            }
            mRealAdapter.onViewRecycled(holder);
        }

        @SuppressWarnings("unchecked")
        @Override
        public boolean onFailedToRecycleView(@NonNull ViewHolder holder) {
            if (mRealAdapter == null) {
                return super.onFailedToRecycleView(holder);
            }
            return mRealAdapter.onFailedToRecycleView(holder);
        }

        @SuppressWarnings("unchecked")
        @Override
        public void onViewAttachedToWindow(@NonNull ViewHolder holder) {
            if (mRealAdapter == null) {
                return;
            }
            mRealAdapter.onViewAttachedToWindow(holder);
        }

        @SuppressWarnings("unchecked")
        @Override
        public void onViewDetachedFromWindow(@NonNull ViewHolder holder) {
            if (mRealAdapter == null) {
                return;
            }
            mRealAdapter.onViewDetachedFromWindow(holder);
        }

        /**
         * 添加头部View
         */
        private void addHeaderView(View view) {
            // 不能添加同一个View对象，否则会导致RecyclerView复用异常
            if (!mHeaderViews.contains(view) && !mFooterViews.contains(view)) {
                mHeaderViews.add(view);
                notifyDataSetChanged();
            }
        }

        /**
         * 移除头部View
         */
        private void removeHeaderView(View view) {
            if (mHeaderViews.remove(view)) {
                notifyDataSetChanged();
            }
        }

        /**
         * 添加底部View
         */
        private void addFooterView(View view) {
            // 不能添加同一个View对象，否则会导致RecyclerView复用异常
            if (!mFooterViews.contains(view) && !mHeaderViews.contains(view)) {
                mFooterViews.add(view);
                notifyDataSetChanged();
            }
        }

        /**
         * 移除底部View
         */
        private void removeFooterView(View view) {
            if (mFooterViews.remove(view)) {
                notifyDataSetChanged();
            }
        }

        /**
         * 获取头部View总数
         */
        private int getHeaderViewsCount() {
            return mHeaderViews.size();
        }

        /**
         * 获取底部View总数
         */
        private int getFooterViewsCount() {
            return mFooterViews.size();
        }

        /**
         * 获取头部View集合
         */
        private List<View> getHeaderViews() {
            return mHeaderViews;
        }

        /**
         * 获取底部View集合
         */
        private List<View> getFooterViews() {
            return mFooterViews;
        }
    }

    /**
     * 头部和底部通用的ViewHolder对象
     */
    private static final class WrapViewHolder extends RecyclerView.ViewHolder {

        private WrapViewHolder(View itemView) {
            super(itemView);
        }
    }

    /**
     * 数据改变监听器
     */
    private static final class WrapAdapterDataObserver extends RecyclerView.AdapterDataObserver {

        private final WrapRecyclerAdapter mWrapAdapter;

        private WrapAdapterDataObserver(WrapRecyclerAdapter adapter) {
            mWrapAdapter = adapter;
        }

        @Override
        public void onChanged() {
            mWrapAdapter.notifyDataSetChanged();
        }

        @Override
        public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
            mWrapAdapter.notifyItemRangeChanged(mWrapAdapter.getHeaderViewsCount() + positionStart, itemCount, payload);
        }

        @Override
        public void onItemRangeChanged(int positionStart, int itemCount) {
            mWrapAdapter.notifyItemRangeChanged(mWrapAdapter.getHeaderViewsCount() + positionStart, itemCount);
        }

        @Override
        public void onItemRangeInserted(int positionStart, int itemCount) {
            mWrapAdapter.notifyItemRangeInserted(mWrapAdapter.getHeaderViewsCount() + positionStart, itemCount);
        }

        @Override
        public void onItemRangeRemoved(int positionStart, int itemCount) {
            mWrapAdapter.notifyItemRangeRemoved(mWrapAdapter.getHeaderViewsCount() + positionStart, itemCount);
        }

        @Override
        public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
            mWrapAdapter.notifyItemMoved(mWrapAdapter.getHeaderViewsCount() + fromPosition, mWrapAdapter.getHeaderViewsCount() + toPosition);
        }
    }
}

app/src/main/java/com/buaa/food/app/TitleBarFragment.java
public abstract class TitleBarFragment<A extends AppActivity> extends AppFragment<A>
        implements TitleBarAction {

    /** 标题栏对象 */
    private TitleBar mTitleBar;
    /** 状态栏沉浸 */
    private ImmersionBar mImmersionBar;

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        // 设置标题栏点击监听
        if (getTitleBar() != null) {
            getTitleBar().setOnTitleBarListener(this);
        }

        if (isStatusBarEnabled()) {
            // 初始化沉浸式状态栏
            getStatusBarConfig().init();

            if (getTitleBar() != null) {
                // 设置标题栏沉浸
                ImmersionBar.setTitleBar(this, getTitleBar());
            }
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        if (isStatusBarEnabled()) {
            // 重新初始化状态栏
            getStatusBarConfig().init();
        }
    }

    /**
     * 是否在 Fragment 使用沉浸式
     */
    public boolean isStatusBarEnabled() {
        return false;
    }

    /**
     * 获取状态栏沉浸的配置对象
     */
    @NonNull
    protected ImmersionBar getStatusBarConfig() {
        if (mImmersionBar == null) {
            mImmersionBar = createStatusBarConfig();
        }
        return mImmersionBar;
    }

    /**
     * 初始化沉浸式
     */
    @NonNull
    protected ImmersionBar createStatusBarConfig() {
        return ImmersionBar.with(this)
                // 默认状态栏字体颜色为黑色
                .statusBarDarkFont(isStatusBarDarkFont())
                // 指定导航栏背景颜色
                .navigationBarColor(R.color.white)
                // 状态栏字体和导航栏内容自动变色，必须指定状态栏颜色和导航栏颜色才可以自动变色
                .autoDarkModeEnable(true, 0.2f);
    }

    /**
     * 获取状态栏字体颜色
     */
    protected boolean isStatusBarDarkFont() {
        // 返回真表示黑色字体
        return getAttachActivity().isStatusBarDarkFont();
    }

    @Override
    @Nullable
    public TitleBar getTitleBar() {
        if (mTitleBar == null || !isLoading()) {
            mTitleBar = obtainTitleBar((ViewGroup) getView());
        }
        return mTitleBar;
    }
}

app/src/main/java/com/buaa/food/app/AppActivity.java
public abstract class AppActivity extends BaseActivity
        implements ToastAction, TitleBarAction, OnHttpListener<Object> {

    /** 标题栏对象 */
    private TitleBar mTitleBar;
    /** 状态栏沉浸 */
    private ImmersionBar mImmersionBar;

    /** 加载对话框 */
    private BaseDialog mDialog;
    /** 对话框数量 */
    private int mDialogCount;

    /**
     * 当前加载对话框是否在显示中
     */
    public boolean isShowDialog() {
        return mDialog != null && mDialog.isShowing();
    }

    /**
     * 显示加载对话框
     */
    public void showDialog() {
        if (isFinishing() || isDestroyed()) {
            return;
        }

        mDialogCount++;
        postDelayed(() -> {
            if (mDialogCount <= 0 || isFinishing() || isDestroyed()) {
                return;
            }

            if (mDialog == null) {
                mDialog = new WaitDialog.Builder(this)
                        .setCancelable(false)
                        .create();
            }
            if (!mDialog.isShowing()) {
                mDialog.show();
            }
        }, 300);
    }

    /**
     * 隐藏加载对话框
     */
    public void hideDialog() {
        if (isFinishing() || isDestroyed()) {
            return;
        }

        if (mDialogCount > 0) {
            mDialogCount--;
        }

        if (mDialogCount != 0 || mDialog == null || !mDialog.isShowing()) {
            return;
        }

        mDialog.dismiss();
    }

    @Override
    protected void initLayout() {
        super.initLayout();

        if (getTitleBar() != null) {
            getTitleBar().setOnTitleBarListener(this);
        }

        // 初始化沉浸式状态栏
        if (isStatusBarEnabled()) {
            getStatusBarConfig().init();

            // 设置标题栏沉浸
            if (getTitleBar() != null) {
                ImmersionBar.setTitleBar(this, getTitleBar());
            }
        }
    }

    /**
     * 是否使用沉浸式状态栏
     */
    protected boolean isStatusBarEnabled() {
        return true;
    }

    /**
     * 状态栏字体深色模式
     */
    protected boolean isStatusBarDarkFont() {
        return true;
    }

    /**
     * 获取状态栏沉浸的配置对象
     */
    @NonNull
    public ImmersionBar getStatusBarConfig() {
        if (mImmersionBar == null) {
            mImmersionBar = createStatusBarConfig();
        }
        return mImmersionBar;
    }

    /**
     * 初始化沉浸式状态栏
     */
    @NonNull
    protected ImmersionBar createStatusBarConfig() {
        return ImmersionBar.with(this)
                // 默认状态栏字体颜色为黑色
                .statusBarDarkFont(isStatusBarDarkFont())
                // 指定导航栏背景颜色
                .navigationBarColor(R.color.white)
                // 状态栏字体和导航栏内容自动变色，必须指定状态栏颜色和导航栏颜色才可以自动变色
                .autoDarkModeEnable(true, 0.2f);
    }

    /**
     * 设置标题栏的标题
     */
    @Override
    public void setTitle(@StringRes int id) {
        setTitle(getString(id));
    }

    /**
     * 设置标题栏的标题
     */
    @Override
    public void setTitle(CharSequence title) {
        super.setTitle(title);
        if (getTitleBar() != null) {
            getTitleBar().setTitle(title);
        }
    }

    @Override
    @Nullable
    public TitleBar getTitleBar() {
        if (mTitleBar == null) {
            mTitleBar = obtainTitleBar(getContentView());
        }
        return mTitleBar;
    }

    @Override
    public void onLeftClick(View view) {
        onBackPressed();
    }

    @Override
    public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {
        super.startActivityForResult(intent, requestCode, options);
        overridePendingTransition(R.anim.right_in_activity, R.anim.right_out_activity);
    }

    @Override
    public void finish() {
        super.finish();
        overridePendingTransition(R.anim.left_in_activity, R.anim.left_out_activity);
    }

    /**
     * {@link OnHttpListener}
     */

    @Override
    public void onStart(Call call) {
        showDialog();
    }

    @Override
    public void onSucceed(Object result) {
        if (result instanceof HttpData) {
            toast(((HttpData<?>) result).getMessage());
        }
    }

    @Override
    public void onFail(Exception e) {
        toast(e.getMessage());
    }

    @Override
    public void onEnd(Call call) {
        hideDialog();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (isShowDialog()) {
            hideDialog();
        }
        mDialog = null;
    }
}

app/src/main/java/com/buaa/food/DishPreview.java
public class DishPreview implements Comparable<DishPreview> {
    int dishId;
    int ordered;
    int viewed;
    String dishName;
    String dishPrice;

    // TODO: implement dishImage
    // String dishImage;
    byte[] image;

    public DishPreview(int dishId, String dishName, String dishPrice, byte[] image,
                       int ordered, int viewed) {
        this.dishId = dishId;
        this.dishName = dishName;
        this.dishPrice = dishPrice;
        this.image = image;
        this.ordered = ordered;
        this.viewed = viewed;
    }

    public DishPreview(String dishName, String dishPrice) {
        this.dishName = dishName;
        this.dishPrice = dishPrice;
    }

    public String getDishName() {
        return dishName;
    }

    public String getDishPrice() {
        return dishPrice;
    }

    public void setDishPrice(String dishPrice) {
        this.dishPrice = dishPrice;
    }

    public byte[] getImage() {
        return image;
    }

    public int getDishId() {
        return dishId;
    }

    @Override
    public int compareTo(DishPreview other) {
        // 比较规则：首先按照ordered属性升序排列，如果相同则按照viewed属性升序排列
        if (this.ordered + this.viewed != other.ordered + other.viewed) {
            return Integer.compare(other.ordered + other.viewed, this.ordered + this.viewed);
        } else {
            return Integer.compare(this.dishId, other.dishId);
        }
    }
}

library/base/src/main/java/com/hjq/base/BaseAdapter.java
public abstract class BaseAdapter<VH extends BaseAdapter<?>.ViewHolder>
        extends RecyclerView.Adapter<VH> implements ResourcesAction {

    /** 上下文对象 */
    private final Context mContext;

    /** RecyclerView 对象 */
    private RecyclerView mRecyclerView;

    /** 条目点击监听器 */
    @Nullable
    private OnItemClickListener mItemClickListener;
    /** 条目长按监听器 */
    @Nullable
    private OnItemLongClickListener mItemLongClickListener;

    /** 条目子 View 点击监听器 */
    @Nullable
    private SparseArray<OnChildClickListener> mChildClickListeners;
    /** 条目子 View 长按监听器 */
    @Nullable
    private SparseArray<OnChildLongClickListener> mChildLongClickListeners;

    /** ViewHolder 位置偏移值 */
    private int mPositionOffset = 0;

    public BaseAdapter(Context context) {
        mContext = context;
        if (mContext == null) {
            throw new IllegalArgumentException("are you ok?");
        }
    }

    @Override
    public final void onBindViewHolder(@NonNull VH holder, int position) {
        // 根据 ViewHolder 绑定的位置和传入的位置进行对比
        // 一般情况下这两个位置值是相等的，但是有一种特殊的情况
        // 在外层添加头部 View 的情况下，这两个位置值是不对等的
        mPositionOffset = position - holder.getAdapterPosition();
        holder.onBindView(position);
    }

    /**
     * 获取 RecyclerView 对象
     */
    @Nullable
    public RecyclerView getRecyclerView() {
        return mRecyclerView;
    }

    @Override
    public Context getContext() {
        return mContext;
    }

    /**
     * 条目 ViewHolder，需要子类 ViewHolder 继承
     */
    public abstract class ViewHolder extends RecyclerView.ViewHolder
            implements View.OnClickListener, View.OnLongClickListener {

        public ViewHolder(@LayoutRes int id) {
            this(LayoutInflater.from(getContext()).inflate(id, mRecyclerView, false));
        }

        public ViewHolder(View itemView) {
            super(itemView);

            // 设置条目的点击和长按事件
            if (mItemClickListener != null) {
                itemView.setOnClickListener(this);
            }
            if (mItemLongClickListener != null) {
                itemView.setOnLongClickListener(this);
            }

            // 设置条目子 View 点击事件
            if (mChildClickListeners != null) {
                for (int i = 0; i < mChildClickListeners.size(); i++) {
                    View childView = findViewById(mChildClickListeners.keyAt(i));
                    if (childView != null) {
                        childView.setOnClickListener(this);
                    }
                }
            }

            // 设置条目子 View 长按事件
            if (mChildLongClickListeners != null) {
                for (int i = 0; i < mChildLongClickListeners.size(); i++) {
                    View childView = findViewById(mChildLongClickListeners.keyAt(i));
                    if (childView != null) {
                        childView.setOnLongClickListener(this);
                    }
                }
            }
        }

        /**
         * 数据绑定回调
         */
        public abstract void onBindView(int position);

        /**
         * 获取 ViewHolder 位置
         */
        protected final int getViewHolderPosition() {
            // 这里解释一下为什么用 getLayoutPosition 而不用 getAdapterPosition
            // 如果是使用 getAdapterPosition 会导致一个问题，那就是快速点击删除条目的时候会出现 -1 的情况，因为这个 ViewHolder 已经解绑了
            // 而使用 getLayoutPosition 则不会出现位置为 -1 的情况，因为解绑之后在布局中不会立马消失，所以不用担心在动画执行中获取位置有异常的情况
            return getLayoutPosition() + mPositionOffset;
        }

        /**
         * {@link View.OnClickListener}
         */

        @Override
        public void onClick(View view) {
            int position = getViewHolderPosition();
            if (position < 0 || position >= getItemCount()) {
                return;
            }

            if (view == getItemView()) {
                if(mItemClickListener != null) {
                    mItemClickListener.onItemClick(mRecyclerView, view, position);
                }
                return;
            }

            if (mChildClickListeners != null) {
                OnChildClickListener listener = mChildClickListeners.get(view.getId());
                if (listener != null) {
                    listener.onChildClick(mRecyclerView, view, position);
                }
            }
        }

        /**
         * {@link View.OnLongClickListener}
         */

        @Override
        public boolean onLongClick(View view) {
            int position = getViewHolderPosition();
            if (position < 0 || position >= getItemCount()) {
                return false;
            }

            if (view == getItemView()) {
                if (mItemLongClickListener != null) {
                    return mItemLongClickListener.onItemLongClick(mRecyclerView, view, position);
                }
                return false;
            }

            if (mChildLongClickListeners != null) {
                OnChildLongClickListener listener = mChildLongClickListeners.get(view.getId());
                if (listener != null) {
                    return listener.onChildLongClick(mRecyclerView, view, position);
                }
            }
            return false;
        }

        public final View getItemView() {
            return itemView;
        }

        public final <V extends View> V findViewById(@IdRes int id) {
            return getItemView().findViewById(id);
        }
    }

    @Override
    public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
        mRecyclerView = recyclerView;
        // 判断当前的布局管理器是否为空，如果为空则设置默认的布局管理器
        if (mRecyclerView.getLayoutManager() == null) {
            RecyclerView.LayoutManager layoutManager = generateDefaultLayoutManager(mContext);
            if (layoutManager != null) {
                mRecyclerView.setLayoutManager(layoutManager);
            }
        }
    }

    @Override
    public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {
        mRecyclerView = null;
    }

    /**
     * 生成默认的布局摆放器
     */
    protected RecyclerView.LayoutManager generateDefaultLayoutManager(Context context) {
        return new LinearLayoutManager(context);
    }

    /**
     * 设置 RecyclerView 条目点击监听
     */
    public void setOnItemClickListener(@Nullable OnItemClickListener listener) {
        checkRecyclerViewState();
        mItemClickListener = listener;
    }

    /**
     * 设置 RecyclerView 条目子 View 点击监听
     */
    public void setOnChildClickListener(@IdRes int id, @Nullable OnChildClickListener listener) {
        checkRecyclerViewState();
        if (mChildClickListeners == null) {
            mChildClickListeners = new SparseArray<>();
        }
        mChildClickListeners.put(id, listener);
    }

    /**
     * 设置 RecyclerView 条目长按监听
     */
    public void setOnItemLongClickListener(@Nullable OnItemLongClickListener listener) {
        checkRecyclerViewState();
        mItemLongClickListener = listener;
    }

    /**
     * 设置 RecyclerView 条目子 View 长按监听
     */
    public void setOnChildLongClickListener(@IdRes int id, @Nullable OnChildLongClickListener listener) {
        checkRecyclerViewState();
        if (mChildLongClickListeners == null) {
            mChildLongClickListeners = new SparseArray<>();
        }
        mChildLongClickListeners.put(id, listener);
    }

    /**
     * 检查 RecyclerView 状态
     */
    private void checkRecyclerViewState() {
        if (mRecyclerView != null) {
            // 必须在 RecyclerView.setAdapter() 之前设置监听
            throw new IllegalStateException("are you ok?");
        }
    }

    /**
     * RecyclerView 条目点击监听类
     */
    public interface OnItemClickListener{

        /**
         * 当 RecyclerView 某个条目被点击时回调
         *
         * @param recyclerView      RecyclerView 对象
         * @param itemView          被点击的条目对象
         * @param position          被点击的条目位置
         */
        void onItemClick(RecyclerView recyclerView, View itemView, int position);
    }

    /**
     * RecyclerView 条目长按监听类
     */
    public interface OnItemLongClickListener {

        /**
         * 当 RecyclerView 某个条目被长按时回调
         *
         * @param recyclerView      RecyclerView 对象
         * @param itemView          被点击的条目对象
         * @param position          被点击的条目位置
         * @return                  是否拦截事件
         */
        boolean onItemLongClick(RecyclerView recyclerView, View itemView, int position);
    }

    /**
     * RecyclerView 条目子 View 点击监听类
     */
    public interface OnChildClickListener {

        /**
         * 当 RecyclerView 某个条目 子 View 被点击时回调
         *
         * @param recyclerView      RecyclerView 对象
         * @param childView         被点击的条目子 View
         * @param position          被点击的条目位置
         */
        void onChildClick(RecyclerView recyclerView, View childView, int position);
    }

    /**
     * RecyclerView 条目子 View 长按监听类
     */
    public interface OnChildLongClickListener {

        /**
         * 当 RecyclerView 某个条目子 View 被长按时回调
         *
         * @param recyclerView      RecyclerView 对象
         * @param childView         被点击的条目子 View
         * @param position          被点击的条目位置
         */
        boolean onChildLongClick(RecyclerView recyclerView, View childView, int position);
    }
}

app/src/main/java/com/buaa/food/ui/activity/CommentActivity.java
public class CommentActivity extends AppActivity implements TextView.OnEditorActionListener{
    private int dishId;
    private int commentId;
    private TitleBar mTitleBar;
    private LinearLayout mLinearLayout;
    private LinearLayout mCommentLinearLayout;
    private TextView commentView;
    private TextView userView;
    private ViewPager mViewPager;
    private FragmentPagerAdapter<AppFragment<?>> mPagerAdapter;
    private DataBaseHelper dataBaseHelper;
    private AppCompatButton mCommentButton;

    protected int getLayoutId() {
        return R.layout.comment_activity;
    }

    protected void initView() {
        dataBaseHelper = new DataBaseHelper(this);

        dishId = getIntent().getIntExtra("dishId", 0);
        commentId = getIntent().getIntExtra("commentId", 0);

        mLinearLayout = findViewById(R.id.comment_activity);
        mCommentLinearLayout = findViewById(R.id.one_comment);

        mTitleBar = findViewById(R.id.comment);
        mViewPager = findViewById(R.id.vp_comment_pager);
        mPagerAdapter = new FragmentPagerAdapter<>(this);

        if (dishId != 0 && commentId == 0) {
            mTitleBar.setTitle("一级评论");
            mPagerAdapter.addFragment(CommentsFragment.newInstance(dishId, CommentsFragment.StatusType.CommentOne));
            mLinearLayout.removeView(mCommentLinearLayout);
        } else if (dishId == 0 && commentId != 0) {
            mTitleBar.setTitle("二级评论");
            mPagerAdapter.addFragment(CommentsFragment.newInstance(commentId, CommentsFragment.StatusType.CommentTwo));
            String comment = dataBaseHelper.getComment(commentId);
            String userName = dataBaseHelper.getCommentUserName(commentId);
            String time = dataBaseHelper.getCommentTime(commentId);
            commentView = findViewById(R.id.tv_comment_content);
            userView = findViewById(R.id.tv_comment_time);
            commentView.setText(comment);
            userView.setText(userName + "  " + time);
        }

        mViewPager.setAdapter(mPagerAdapter);

        mCommentButton = findViewById(R.id.btn_comment);

        setOnClickListener(mCommentButton);
    }

    protected void initData() {
    }

    @SingleClick
    @Override
    public void onClick(View view) {
        if (view == mCommentButton) {
            new InputDialog.Builder(this)
                    .setTitle("评论")
                    .setContent("请输入评论内容")
                    .setHint("评论内容")
                    .setConfirm("确定")
                    .setCancel("取消")
                    .setAutoDismiss(true)
                    .setListener((dialog, content) -> {
                        if (content.isEmpty()) {
                            toast("请输入评论内容");
                        }
                        if (dishId != 0 && commentId == 0) {
                            dataBaseHelper.insertComment(dishId, content, dataBaseHelper
                                    .getUserId(UserAuth.getLocalUserPhone()));
                            toast("评论成功");
                            finish();
                        } else if (dishId == 0 && commentId != 0) {
                            dataBaseHelper.insertSecondComment(commentId, content, dataBaseHelper
                                    .getUserId(UserAuth.getLocalUserPhone()));
                            toast("评论成功");
                            finish();
                        }
                    })
                    .show();
        }
    }

    @Override
    public boolean onEditorAction(TextView textView, int i, KeyEvent keyEvent) {
        return false;
    }
}

app/src/main/java/com/buaa/food/ui/activity/admin/DishUploadActivity.java
public final class DishUploadActivity extends AppActivity
        implements TextView.OnEditorActionListener {

    private int dishId;

    private AppCompatImageView mImageView;
    private EditText mNameView;
    private EditText mPriceView;
    private EditText mRemainingView;
    private AppCompatTextView mCanteenView;
    private AppCompatTextView mWindowView;
    private SubmitButton mUploadView;

    private DataBaseHelper dataBaseHelper;

    @Override
    protected int getLayoutId() {
        return R.layout.dish_upload_activity;
    }

    @Override
    protected void initView() {
        dishId = getIntent().getIntExtra("dishId", -1);

        mImageView = findViewById(R.id.iv_dish_image);
        mNameView = findViewById(R.id.et_dish_name);
        mPriceView = findViewById(R.id.et_dish_price);
        mRemainingView = findViewById(R.id.et_dish_remaining);
        mCanteenView = findViewById(R.id.tv_canteen);
        mWindowView = findViewById(R.id.tv_window);
        mUploadView = findViewById(R.id.btn_dish_upload);

        dataBaseHelper = new DataBaseHelper(this);

        ImmersionBar.setTitleBar(this, findViewById(R.id.tv_register_title));

        InputTextManager.with(this)
            .addView(mNameView)
            .addView(mPriceView)
            .addView(mRemainingView)
            .setMain(mUploadView)
            .build();

        setOnClickListener(mImageView, mCanteenView, mWindowView, mUploadView);
    }

    @Override
    protected void initData() {
        if (dishId == -1) {
            // 添加菜品
            GlideApp.with(getActivity())
                .load(R.drawable.baseline_fastfood_24)
                .placeholder(R.drawable.baseline_fastfood_24)
                .error(R.drawable.baseline_fastfood_24)
                .transform(new MultiTransformation<>(new CenterCrop(), new CircleCrop()))
                .into(mImageView);
        } else {
            // 修改菜品
            byte[] image = dataBaseHelper.getDishImage(dishId);
            if (image != null && image.length > 0) {
                Bitmap imgBitmap = BitmapFactory.decodeByteArray(image, 0, image.length);

                GlideApp.with(getActivity())
                    .load(imgBitmap)
                    .placeholder(R.drawable.baseline_fastfood_24)
                    .error(R.drawable.baseline_fastfood_24)
                    .transform(new MultiTransformation<>(new CenterCrop(), new CircleCrop()))
                    .into(mImageView);
            } else {
                GlideApp.with(getActivity())
                    .load(R.drawable.baseline_fastfood_24)
                    .placeholder(R.drawable.baseline_fastfood_24)
                    .error(R.drawable.baseline_fastfood_24)
                    .transform(new MultiTransformation<>(new CenterCrop(), new CircleCrop()))
                    .into(mImageView);
            }

            mNameView.setText(dataBaseHelper.getDishName(dishId));
            mPriceView.setText(String.valueOf(dataBaseHelper.getDishPrice(dishId)));
            mRemainingView.setText(String.valueOf(dataBaseHelper.getDishRemaining(dishId)));
            mCanteenView.setText(dataBaseHelper.getDishCanteen(dishId));
            mWindowView.setText(dataBaseHelper.getDishWindow(dishId));
        }
    }

    @SingleClick
    @Override
    public void onClick(View view) {
        String canteen = mCanteenView.getText().toString();

        if (view == mUploadView) {

            String dishName = mNameView.getText().toString();
            float dishPrice = Float.parseFloat(mPriceView.getText().toString());
            int dishRemaining = Integer.parseInt(mRemainingView.getText().toString());
            int canteenId = dataBaseHelper.getCanteenId(canteen);
            int windowId = dataBaseHelper.getWindowId(mWindowView.getText().toString());

            hideKeyboard(getCurrentFocus()); // 隐藏软键盘

            if (dishId == -1) {
                dataBaseHelper.insertDish(dishName, dishPrice, dishRemaining, windowId, canteenId);
                Toast.makeText(DishUploadActivity.this, "Upload Successfully", Toast.LENGTH_SHORT).show();
            } else {
                dataBaseHelper.updateDish(dishId, dishName, dishPrice, dishRemaining, windowId, canteenId);
                Toast.makeText(DishUploadActivity.this, "Edit Successfully", Toast.LENGTH_SHORT).show();
            }

            mUploadView.showSucceed();
            finish();

        } else if (view == mImageView) {
            ImageSelectActivity.start(this, data -> {
                cropImageFile(new File(data.get(0)));
            });

        } else if (view == mCanteenView) {
//            mWindowView.setText("选择窗口");

            int defaultSelect = 0;
            switch (canteen) {
                case "学三食堂（B1层）":
                    break;
                case "学五食堂（一层）":
                    defaultSelect = 1;
                    break;
                case "学六食堂（二层）":
                    defaultSelect = 2;
                    break;
                case "教工食堂（二层）":
                    defaultSelect = 3;
                    break;
            }

            new SelectDialog.Builder(this)
                .setTitle("请选择菜品食堂")
                .setList("学三食堂（B1层）", "学五食堂（一层）", "学六食堂（二层）", "教工食堂（二层）")
                .setSingleSelect()
                .setSelect(defaultSelect)
                .setListener(new SelectDialog.OnListener<String>() {

                    @Override
                    public void onSelected(BaseDialog dialog, HashMap<Integer, String> data) {
                        mCanteenView.setText(data.values().iterator().next());
                    }

                    @Override
                    public void onCancel(BaseDialog dialog) {
                        toast("取消选择");
                    }
                })
                .show();

        } else if (view == mWindowView) {
            if (canteen.equals("选择食堂")) {
                toast("请先选择食堂");
            } else {
                switch (canteen) {
                    case "学三食堂（B1层）":
                        new SelectDialog.Builder(this)
                            .setTitle("请选择菜品窗口")
                            .setList(
                                    "三颗糖",
                                    "不止一面",
                                    "原蛊蒸饭",
                                    "新疆炒米粉",
                                    "日鉴一面（牛肉面）",
                                    "炉石披萨",
                                    "炸鸡汉堡",
                                    "猪肚鸡",
                                    "王宏龙馋嘴鱼",
                                    "米大碗",
                                    "缘味先石锅饭",
                                    "胡椒小屋"
                            )
                            .setSingleSelect()
                            .setSelect(0)
                            .setListener(new SelectDialog.OnListener<String>() {

                                @Override
                                public void onSelected(BaseDialog dialog, HashMap<Integer, String> data) {
                                    mWindowView.setText(data.values().iterator().next());
                                }

                                @Override
                                public void onCancel(BaseDialog dialog) {
                                    toast("取消选择");
                                }
                            })
                            .show();
                        break;
                    case "学五食堂（一层）":
                        new SelectDialog.Builder(this)
                            .setTitle("请选择菜品窗口")
                            .setList(
                                    "主食",
                                    "卤肉饭",
                                    "基本伙",
                                    "小碗菜",
                                    "渔粉",
                                    "滋补汤品",
                                    "熟食",
                                    "精品菜",
                                    "航味菜",
                                    "轻食套餐",
                                    "铁板拌饭",
                                    "面条",
                                    "麻辣香锅冒菜"
                            )
                            .setSingleSelect()
                            .setSelect(0)
                            .setListener(new SelectDialog.OnListener<String>() {

                                @Override
                                public void onSelected(BaseDialog dialog, HashMap<Integer, String> data) {
                                    mWindowView.setText(data.values().iterator().next());
                                }

                                @Override
                                public void onCancel(BaseDialog dialog) {
                                    toast("取消选择");
                                }
                            })
                            .show();
                        break;
                    case "学六食堂（二层）":
                        new SelectDialog.Builder(this)
                            .setTitle("请选择菜品窗口")
                            .setList(
                                    "一品粥饼",
                                    "五谷渔粉",
                                    "千里香馄饨",
                                    "咖喱饭",
                                    "广式烧腊",
                                    "桂林米粉",
                                    "湖南小碗菜",
                                    "炒河粉",
                                    "煎饼豆浆",
                                    "猛火炒饭",
                                    "石锅拌饭",
                                    "酸菜鱼",
                                    "铁板饭",
                                    "闽南浇汁拌饭",
                                    "鱼籽饭",
                                    "黄焖鸡米饭"
                            )
                            .setSingleSelect()
                            .setSelect(0)
                            .setListener(new SelectDialog.OnListener<String>() {

                                @Override
                                public void onSelected(BaseDialog dialog, HashMap<Integer, String> data) {
                                    mWindowView.setText(data.values().iterator().next());
                                }

                                @Override
                                public void onCancel(BaseDialog dialog) {
                                    toast("取消选择");
                                }
                            })
                            .show();
                        break;
                    case "教工食堂（二层）":
                        new SelectDialog.Builder(this)
                            .setTitle("请选择菜品窗口")
                            .setList(
                                    "统一窗口"
                            )
                            .setSingleSelect()
                            .setSelect(0)
                            .setListener(new SelectDialog.OnListener<String>() {

                                @Override
                                public void onSelected(BaseDialog dialog, HashMap<Integer, String> data) {
                                    mWindowView.setText(data.values().iterator().next());
                                }

                                @Override
                                public void onCancel(BaseDialog dialog) {
                                    toast("取消选择");
                                }
                            })
                            .show();
                        break;
                }
            }
        }
    }

    @NonNull
    @Override
    protected ImmersionBar createStatusBarConfig() {
        return super.createStatusBarConfig()
            .navigationBarColor(R.color.white)
            .keyboardEnable(true);
    }

    @Override
    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
        return false;
    }

    private void cropImageFile(File sourceFile) {
        ImageCropActivity.start(this, sourceFile, 1, 1, new ImageCropActivity.OnCropListener() {

            @Override
            public void onSucceed(Uri fileUri, String fileName) {
                File outputFile;
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                    outputFile = new FileContentResolver(getActivity(), fileUri, fileName);
                } else {
                    try {
                        outputFile = new File(new URI(fileUri.toString()));
                    } catch (URISyntaxException e) {
                        e.printStackTrace();
                        outputFile = new File(fileUri.toString());
                    }
                }
                updateCropImage(outputFile, true);
            }

            @Override
            public void onError(String details) {
                updateCropImage(sourceFile, false);
            }
        });
    }

    private void updateCropImage(File file, boolean deleteFile) {
        Uri mAvatarUrl;
        if (file instanceof FileContentResolver) {
            mAvatarUrl = ((FileContentResolver) file).getContentUri();
        } else {
            mAvatarUrl = Uri.fromFile(file);
        }
        GlideApp.with(getActivity())
            .load(mAvatarUrl)
            .transform(new MultiTransformation<>(new CenterCrop(), new CircleCrop()))
            .into(mImageView);

        try {
            Bitmap bitmapImage = MediaStore.Images.Media.getBitmap(this.getContentResolver(), mAvatarUrl);
            this.storeImage(bitmapImage);
        } catch (Exception e) {
            Toast.makeText(this, "图片转换或上传出现问题", Toast.LENGTH_SHORT).show();
            e.printStackTrace();
        }
    }

    private void storeImage(Bitmap bitmapImage) {
        if (mImageView.getDrawable() != null && bitmapImage != null) {
            dataBaseHelper.updateDishImage(dishId, bitmapImage);
        } else {
            Toast.makeText(this, "图片为空", Toast.LENGTH_SHORT).show();
        }
    }
}

Answer: List<CommentPreview> data = new ArrayList<>();

<think>
The method analogData() should start by creating a list to hold CommentPreview objects before populating it. The immediate next line after the method signature is the declaration and initialization of that list.
</think>
List<CommentPreview> list = new ArrayList<>();