[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 rikka.core.os.FileUtils;
import rikka.core.res.ResourcesKt;
import rikka.insets.WindowInsetsHelperKt;
import rikka.recyclerview.RecyclerViewKt;
@SuppressLint("NotifyDataSetChanged")
@ -132,16 +134,15 @@ public class LogsActivity extends BaseActivity {
.setCancelable(false)
.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();
RecyclerViewKt.fixEdgeEffect(binding.recyclerView, false, true);
binding.recyclerView.setAdapter(adapter);
layoutManager = new LinearLayoutManagerFix(this);
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() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
@ -224,7 +225,6 @@ public class LogsActivity extends BaseActivity {
private void clear() {
if (ConfigManager.clearLogs(verbose)) {
adapter.setEmpty();
Snackbar.make(binding.snackbar, R.string.logs_cleared, Snackbar.LENGTH_SHORT).show();
reloadErrorLog();
} else {
@ -316,11 +316,8 @@ public class LogsActivity extends BaseActivity {
@Override
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
if (mProgressDialog.isShowing()) {
mProgressDialog.dismiss();
@ -349,7 +346,6 @@ public class LogsActivity extends BaseActivity {
if (binding.recyclerView.getWidth() < desiredWidth) {
binding.recyclerView.requestLayout();
}
}
void setLogs(List<String> logs) {
@ -358,12 +354,6 @@ public class LogsActivity extends BaseActivity {
notifyDataSetChanged();
}
void setEmpty() {
logs.clear();
logs.add(getString(R.string.log_is_empty));
notifyDataSetChanged();
}
@Override
public int getItemCount() {
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.repo.RepoLoader;
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.LinearLayoutManagerFix;
import org.lsposed.manager.util.ModuleUtil;
@ -211,14 +212,18 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi
@Override
public void onSingleInstalledModuleReloaded() {
adapters.forEach(ModuleAdapter::refresh);
adapters.forEach(adapter -> {
adapter.refresh(true);
});
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
int itemId = item.getItemId();
if (itemId == R.id.menu_refresh) {
adapters.forEach(ModuleAdapter::refresh);
adapters.forEach(adapter -> {
adapter.refresh(true);
});
}
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> showList = new ArrayList<>();
private final int userId;
private boolean isLoaded;
ModuleAdapter(int 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 {
View root;
ImageView appIcon;
@ -522,6 +533,7 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi
showList.clear();
//noinspection unchecked
showList.addAll((List<ModuleUtil.InstalledModule>) results.values);
isLoaded = true;
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:id="@+id/swipeRefreshLayout">
<rikka.widget.borderview.BorderRecyclerView
<org.lsposed.manager.ui.widget.EmptyStateRecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
@ -83,4 +83,4 @@
app:fitSystemWindowsInsets="bottom" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -54,7 +54,7 @@
android:layout_marginTop="?actionBarSize"
app:layout_fitSystemWindowsInsets="top" />
<rikka.widget.borderview.BorderRecyclerView
<org.lsposed.manager.ui.widget.EmptyStateRecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
@ -64,4 +64,4 @@
app:borderTopDrawable="@null"
app:borderBottomVisibility="never"
app:fitSystemWindowsInsets="top|bottom" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

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

View File

@ -18,7 +18,7 @@
~ 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"
android:id="@+id/recyclerView"
android:layout_width="match_parent"
@ -28,4 +28,4 @@
app:borderTopVisibility="whenTop"
app:borderTopDrawable="@null"
app:borderBottomVisibility="never"
app:fitSystemWindowsInsets="top|bottom" />
app:fitSystemWindowsInsets="top|bottom" />

View File

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

View File

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