[app] Add empty view to lists (#575)

This commit is contained in:
tehcneko 2021-05-14 22:05:58 +08:00 committed by GitHub
parent c13f4c90d1
commit 693016ae85
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 132 additions and 29 deletions

View File

@ -71,6 +71,8 @@ import java.util.List;
import java.util.Locale; import java.util.Locale;
import rikka.core.os.FileUtils; import rikka.core.os.FileUtils;
import rikka.core.res.ResourcesKt;
import rikka.insets.WindowInsetsHelperKt;
import rikka.recyclerview.RecyclerViewKt; import rikka.recyclerview.RecyclerViewKt;
@SuppressLint("NotifyDataSetChanged") @SuppressLint("NotifyDataSetChanged")
@ -132,16 +134,15 @@ public class LogsActivity extends BaseActivity {
.setCancelable(false) .setCancelable(false)
.show(); .show();
} }
if (!ConfigManager.isVerboseLogEnabled()) {
WindowInsetsHelperKt.setInitialPadding(binding.recyclerView, 0, ResourcesKt.resolveDimensionPixelOffset(getTheme(), R.attr.actionBarSize, 0), 0, 0);
binding.slidingTabs.setVisibility(View.GONE);
}
adapter = new LogsAdapter(); adapter = new LogsAdapter();
RecyclerViewKt.fixEdgeEffect(binding.recyclerView, false, true); RecyclerViewKt.fixEdgeEffect(binding.recyclerView, false, true);
binding.recyclerView.setAdapter(adapter); binding.recyclerView.setAdapter(adapter);
layoutManager = new LinearLayoutManagerFix(this); layoutManager = new LinearLayoutManagerFix(this);
binding.recyclerView.setLayoutManager(layoutManager); binding.recyclerView.setLayoutManager(layoutManager);
if (!ConfigManager.isVerboseLogEnabled()) {
binding.slidingTabs.setVisibility(View.GONE);
} else {
RecyclerViewKt.addVerticalPadding(binding.recyclerView, 48, 0);
}
binding.slidingTabs.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() { binding.slidingTabs.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override @Override
public void onTabSelected(TabLayout.Tab tab) { public void onTabSelected(TabLayout.Tab tab) {
@ -224,7 +225,6 @@ public class LogsActivity extends BaseActivity {
private void clear() { private void clear() {
if (ConfigManager.clearLogs(verbose)) { if (ConfigManager.clearLogs(verbose)) {
adapter.setEmpty();
Snackbar.make(binding.snackbar, R.string.logs_cleared, Snackbar.LENGTH_SHORT).show(); Snackbar.make(binding.snackbar, R.string.logs_cleared, Snackbar.LENGTH_SHORT).show();
reloadErrorLog(); reloadErrorLog();
} else { } else {
@ -316,11 +316,8 @@ public class LogsActivity extends BaseActivity {
@Override @Override
protected void onPostExecute(List<String> logs) { protected void onPostExecute(List<String> logs) {
if (logs.size() == 0) {
adapter.setEmpty();
} else {
adapter.setLogs(logs); adapter.setLogs(logs);
}
handler.removeCallbacks(mRunnable);//It loaded so fast that no need to show progress handler.removeCallbacks(mRunnable);//It loaded so fast that no need to show progress
if (mProgressDialog.isShowing()) { if (mProgressDialog.isShowing()) {
mProgressDialog.dismiss(); mProgressDialog.dismiss();
@ -349,7 +346,6 @@ public class LogsActivity extends BaseActivity {
if (binding.recyclerView.getWidth() < desiredWidth) { if (binding.recyclerView.getWidth() < desiredWidth) {
binding.recyclerView.requestLayout(); binding.recyclerView.requestLayout();
} }
} }
void setLogs(List<String> logs) { void setLogs(List<String> logs) {
@ -358,12 +354,6 @@ public class LogsActivity extends BaseActivity {
notifyDataSetChanged(); notifyDataSetChanged();
} }
void setEmpty() {
logs.clear();
logs.add(getString(R.string.log_is_empty));
notifyDataSetChanged();
}
@Override @Override
public int getItemCount() { public int getItemCount() {
return logs.size(); return logs.size();

View File

@ -72,6 +72,7 @@ import org.lsposed.manager.databinding.ActivityModuleDetailBinding;
import org.lsposed.manager.databinding.ItemRepoRecyclerviewBinding; import org.lsposed.manager.databinding.ItemRepoRecyclerviewBinding;
import org.lsposed.manager.repo.RepoLoader; import org.lsposed.manager.repo.RepoLoader;
import org.lsposed.manager.ui.activity.base.BaseActivity; import org.lsposed.manager.ui.activity.base.BaseActivity;
import org.lsposed.manager.ui.widget.EmptyStateRecyclerView;
import org.lsposed.manager.util.GlideApp; import org.lsposed.manager.util.GlideApp;
import org.lsposed.manager.util.LinearLayoutManagerFix; import org.lsposed.manager.util.LinearLayoutManagerFix;
import org.lsposed.manager.util.ModuleUtil; import org.lsposed.manager.util.ModuleUtil;
@ -211,14 +212,18 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi
@Override @Override
public void onSingleInstalledModuleReloaded() { public void onSingleInstalledModuleReloaded() {
adapters.forEach(ModuleAdapter::refresh); adapters.forEach(adapter -> {
adapter.refresh(true);
});
} }
@Override @Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) { public boolean onOptionsItemSelected(@NonNull MenuItem item) {
int itemId = item.getItemId(); int itemId = item.getItemId();
if (itemId == R.id.menu_refresh) { if (itemId == R.id.menu_refresh) {
adapters.forEach(ModuleAdapter::refresh); adapters.forEach(adapter -> {
adapter.refresh(true);
});
} }
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
@ -322,10 +327,11 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi
} }
} }
private class ModuleAdapter extends RecyclerView.Adapter<ModuleAdapter.ViewHolder> implements Filterable { private class ModuleAdapter extends EmptyStateRecyclerView.EmptyStateAdapter<ModuleAdapter.ViewHolder> implements Filterable {
private final List<ModuleUtil.InstalledModule> searchList = new ArrayList<>(); private final List<ModuleUtil.InstalledModule> searchList = new ArrayList<>();
private final List<ModuleUtil.InstalledModule> showList = new ArrayList<>(); private final List<ModuleUtil.InstalledModule> showList = new ArrayList<>();
private final int userId; private final int userId;
private boolean isLoaded;
ModuleAdapter(int userId) { ModuleAdapter(int userId) {
this.userId = userId; this.userId = userId;
@ -470,6 +476,11 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi
} }
}; };
@Override
public boolean isLoaded() {
return isLoaded;
}
class ViewHolder extends RecyclerView.ViewHolder { class ViewHolder extends RecyclerView.ViewHolder {
View root; View root;
ImageView appIcon; ImageView appIcon;
@ -522,6 +533,7 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi
showList.clear(); showList.clear();
//noinspection unchecked //noinspection unchecked
showList.addAll((List<ModuleUtil.InstalledModule>) results.values); showList.addAll((List<ModuleUtil.InstalledModule>) results.values);
isLoaded = true;
notifyDataSetChanged(); notifyDataSetChanged();
} }
} }

View File

@ -0,0 +1,97 @@
package org.lsposed.manager.ui.widget;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.text.Layout;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import androidx.annotation.Nullable;
import org.lsposed.manager.R;
import rikka.core.res.ResourcesKt;
import rikka.widget.borderview.BorderRecyclerView;
public class EmptyStateRecyclerView extends BorderRecyclerView {
private final TextPaint paint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
private final String emptyText;
private final AdapterDataObserver emptyObserver = new AdapterDataObserver() {
@Override
public void onChanged() {
Adapter<?> adapter = getAdapter();
if (adapter != null) {
boolean newEmpty = adapter.getItemCount() == 0;
if (empty != newEmpty) {
empty = newEmpty;
invalidate();
}
}
}
};
private boolean empty = false;
public EmptyStateRecyclerView(Context context) {
this(context, null);
}
public EmptyStateRecyclerView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public EmptyStateRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
DisplayMetrics dm = context.getResources().getDisplayMetrics();
paint.setColor(ResourcesKt.resolveColor(context.getTheme(), android.R.attr.textColorSecondary));
paint.setTextSize(16f * dm.scaledDensity);
emptyText = context.getString(R.string.list_empty);
}
@Override
public void setAdapter(Adapter adapter) {
var oldAdapter = getAdapter();
if (oldAdapter != null) {
oldAdapter.unregisterAdapterDataObserver(emptyObserver);
}
super.setAdapter(adapter);
if (adapter != null) {
adapter.registerAdapterDataObserver(emptyObserver);
if (adapter instanceof EmptyStateAdapter && ((EmptyStateAdapter<?>) adapter).isLoaded()) {
emptyObserver.onChanged();
}
}
}
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
if (empty) {
final int width = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
final int height = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
var textLayout = new StaticLayout(emptyText, paint, width, Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false);
canvas.save();
canvas.translate(getPaddingLeft(), (height >> 1) + getPaddingTop() - (textLayout.getHeight() >> 1));
textLayout.draw(canvas);
canvas.restore();
}
}
public abstract static class EmptyStateAdapter<T extends ViewHolder> extends Adapter<T> {
abstract public boolean isLoaded();
}
}

View File

@ -72,7 +72,7 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:id="@+id/swipeRefreshLayout"> android:id="@+id/swipeRefreshLayout">
<rikka.widget.borderview.BorderRecyclerView <org.lsposed.manager.ui.widget.EmptyStateRecyclerView
android:id="@+id/recyclerView" android:id="@+id/recyclerView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"

View File

@ -54,7 +54,7 @@
android:layout_marginTop="?actionBarSize" android:layout_marginTop="?actionBarSize"
app:layout_fitSystemWindowsInsets="top" /> app:layout_fitSystemWindowsInsets="top" />
<rikka.widget.borderview.BorderRecyclerView <org.lsposed.manager.ui.widget.EmptyStateRecyclerView
android:id="@+id/recyclerView" android:id="@+id/recyclerView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"

View File

@ -71,14 +71,15 @@
android:id="@+id/horizontalScrollView" android:id="@+id/horizontalScrollView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:fillViewport="true"
android:scrollbars="none"> android:scrollbars="none">
<rikka.widget.borderview.BorderRecyclerView <org.lsposed.manager.ui.widget.EmptyStateRecyclerView
android:id="@+id/recyclerView" android:id="@+id/recyclerView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="match_parent"
android:clipToPadding="false" android:clipToPadding="false"
android:paddingTop="?actionBarSize" android:paddingTop="104dp"
app:borderTopVisibility="whenTop" app:borderTopVisibility="whenTop"
app:borderTopDrawable="@null" app:borderTopDrawable="@null"
app:borderBottomVisibility="never" app:borderBottomVisibility="never"

View File

@ -18,7 +18,7 @@
~ Copyright (C) 2021 LSPosed Contributors ~ Copyright (C) 2021 LSPosed Contributors
--> -->
<rikka.widget.borderview.BorderRecyclerView xmlns:android="http://schemas.android.com/apk/res/android" <org.lsposed.manager.ui.widget.EmptyStateRecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/recyclerView" android:id="@+id/recyclerView"
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -85,6 +85,7 @@
<string name="module_uninstall_message">要卸载此模块吗?</string> <string name="module_uninstall_message">要卸载此模块吗?</string>
<string name="module_uninstalled">已卸载%1$s</string> <string name="module_uninstalled">已卸载%1$s</string>
<string name="module_uninstall_failed">卸载失败</string> <string name="module_uninstall_failed">卸载失败</string>
<string name="user_title">用户 %d</string>
<!-- AppListActivity --> <!-- AppListActivity -->
<string name="compile_speed">重新优化</string> <string name="compile_speed">重新优化</string>
@ -177,4 +178,5 @@
<string name="NotInstallDetail">LSPosed 未安装</string> <string name="NotInstallDetail">LSPosed 未安装</string>
<string name="translators"><![CDATA[<a href="https://github.com/LSPosed/LSPosed">LSPosed</a>]]></string> <string name="translators"><![CDATA[<a href="https://github.com/LSPosed/LSPosed">LSPosed</a>]]></string>
<string name="copy_toast_msg">已复制</string> <string name="copy_toast_msg">已复制</string>
<string name="list_empty">¯\\\\_(ツ)_\/¯\n空空如也</string>
</resources> </resources>

View File

@ -179,4 +179,5 @@
<string name="NotInstallDetail">LSPosed is not Installed</string> <string name="NotInstallDetail">LSPosed is not Installed</string>
<string name="translators"><![CDATA[<a href="https://github.com/LSPosed/LSPosed">LSPosed</a>]]></string> <string name="translators"><![CDATA[<a href="https://github.com/LSPosed/LSPosed">LSPosed</a>]]></string>
<string name="copy_toast_msg">Copied</string> <string name="copy_toast_msg">Copied</string>
<string name="list_empty">¯\\\\_(ツ)_\/¯\nNothing here</string>
</resources> </resources>