神马笔记最新版本下载:【神马笔记 版本1.5.0——笔名功能.apk】
随着软件功能的增加,不可避免地会加入越来越多的设置项。如何管理不断增加的设置项?
首先,必修便于创建,修改,删除,并且只需要改动配置项,而不用对界面进行太大的修改。
其次,子设置界面必须与主设置界面保持风格一致。
实现设置界面的方式有很多,可以选择任意的容器和控件进行组合,从而实现设置界面。
但是设置界面有其特殊性。
设置界面通常是有多个设置项组成,每一个设置项又由键值对构成。多个设置项可以组合成一个设置组,设置组之间由分割线区分开。其组成方式大概如下结构。
从以上结构来看,设置界面便是一组设置项的列表。列表界面自然是使用RecyclerView来实现。
定义设置项UI的组成。
public class BaseSettingItem<T> { public static final int DIVIDER_NONE = 0; public static final int DIVIDER_NAME = 1; String id; // 唯一ID @DrawableRes int iconResId; // 图标资源 ColorStateList iconTintList; // 图标Tint CharSequence name; // 名称 CharSequence text; // 文本 boolean chevron; // 是否显示chevron Consumer<T> consumer; // 列表项处理点击事件 T userObject; // 用户数据 boolean dividerVisible; // 是否显示分割线 int dividerType; // 分割线的显示方式 }
定义基础设置项的布局。基础布局包括图标、名称、箭头3个部分组成。
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:minHeight="@dimen/settingItemHeight" android:background="@color/colorSettingItemBackground" android:paddingLeft="?listPreferredItemPaddingLeft" android:paddingRight="?listPreferredItemPaddingRight" android:gravity="center_vertical" android:foreground="?selectableItemBackground"> <ImageView android:id="@+id/iv_icon" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginRight="?listPreferredItemPaddingRight"/> <TextView android:id="@+id/tv_name" android:textAppearance="@style/Base.TextAppearance.AppCompat.Menu" android:layout_width="0dp" android:layout_weight="1" android:layout_height="wrap_content"/> <ImageView android:id="@+id/iv_chevron" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_chevron_right_white_24dp" android:tint="@color/colorChevron"/> </LinearLayout>
RecyclerView的ViewHolder实现。
public class BaseSettingViewHolder<T extends BaseSettingItem> extends BridgeViewHolder<T> implements SectionDividerDecoration.Adapter { public static final int LAYOUT_RES_ID = R.layout.layout_setting_list_item; ImageView iconView; TextView nameView; TextView textView; ImageView chevronView; @Keep public BaseSettingViewHolder(View itemView) { super(itemView); } @Override public int getLayoutResourceId() { return LAYOUT_RES_ID; } @Override public void onViewCreated(@NonNull View view) { view.setOnClickListener(this::onItemClick); this.iconView = view.findViewById(R.id.iv_icon); this.nameView = view.findViewById(R.id.tv_name); this.textView = view.findViewById(R.id.tv_text); this.chevronView = view.findViewById(R.id.iv_chevron); if (iconView != null) { if (iconView.getOutlineProvider() != null) { iconView.setClipToOutline(true); } } } @Override public void onBind(T item, int position) { if (iconView != null) { if (item.getIcon() > 0) { iconView.setImageResource(item.getIcon()); iconView.setVisibility(View.VISIBLE); } else { iconView.setImageDrawable(null); iconView.setVisibility(View.GONE); } iconView.setImageTintList(item.getIconTintList()); } if (nameView != null) { nameView.setText(item.getName()); } if (textView != null) { textView.setText(item.getText()); } if (chevronView != null) { chevronView.setVisibility(item.isChevron()? View.VISIBLE: View.GONE); } } protected void onItemClick(View view) { T item = getItem(); if (item.getConsumer() != null) { item.getConsumer().accept(item.getUserObject()); } } @Override public int getMargin(SectionDividerDecoration decoration) { int margin = 0; int type = getItem().getDividerType(); if (type == BaseSettingItem.DIVIDER_NAME) { if (nameView != null) { margin = nameView.getLeft(); } } return margin; } @Override public boolean isVisible(SectionDividerDecoration decoration) { return getItem().isDividerVisible(); } }
定义设置界面的抽象Fragment,所有设置界面均继承该类,子类必须实现以下2个抽象接口。
List<BaseSettingItem> createList()
创建设置项列表。
void buildAdapter(BridgeAdapter adapter)
构建适配器。
public abstract class BasePreferenceFragment extends Fragment { SearchTitleBar titleBar; RecyclerView recyclerView; BridgeAdapter adapter; SettingProvider provider; SectionDividerDecoration dividerDecoration; public BasePreferenceFragment() { } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_preference, container, false); } @CallSuper @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); { this.titleBar = view.findViewById(R.id.title_bar); Toolbar toolbar = titleBar.getToolbar(); toolbar.setNavigationIcon(R.drawable.ic_arrow_back_white_24dp); toolbar.setNavigationOnClickListener(this::onNavigationClick); } { this.recyclerView = view.findViewById(R.id.recycler_list_view); LinearLayoutManager layout = new LinearLayoutManager(getActivity()); recyclerView.setLayoutManager(layout); } { this.dividerDecoration = new SectionDividerDecoration(getActivity()); recyclerView.addItemDecoration(dividerDecoration); } } @CallSuper @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); { this.provider = new SettingProvider(this.createList()); } { this.adapter = new BridgeAdapter(getActivity(), provider); this.buildAdapter(adapter); } { recyclerView.setAdapter(adapter); } } void onNavigationClick(View view) { getActivity().onBackPressed(); } void setTitle(CharSequence text) { titleBar.setTitle(text); } <T> T getItem(String id) { return provider.getItem(id); } int indexOf(Object object) { return provider.indexOf(object); } void notifyItemChanged(Object object) { int index = indexOf(object); if (index >= 0) { adapter.notifyItemChanged(index); } } abstract List<BaseSettingItem> createList(); abstract void buildAdapter(BridgeAdapter adapter); /** * */ class SettingProvider implements BridgeAdapterProvider<BaseSettingItem> { ArrayList<BaseSettingItem> list; public SettingProvider(List<BaseSettingItem> list) { this.list = new ArrayList<>(list); } <T> T getItem(String id) { BaseSettingItem item = list.stream() .filter(e -> e.getId().equals(id)) .findAny() .orElse(null); return (T)item; } int indexOf(Object object) { int size = list.size(); for (int i = 0; i < size; i++) { if (list.get(i) == object) { return i; } } return -1; } @Override public BaseSettingItem get(int position) { return list.get(position); } @Override public int size() { return list.size(); } } }
实现主设置界面。
List<BaseSettingItem> createList()
添加了3个设置项——ProfileSettingItem、ExplainSettingItem、BaseSettingItem。
void buildAdapter(BridgeAdapter adapter)
绑定了3种ViewHolder——ProfileSettingViewHolder、ExplainSettingItem、BaseSettingViewHolder。
public class SettingFragment extends BasePreferenceFragment { static final String ID_PROFILE = "profile"; RequestResultManager requestResultManager; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.requestResultManager = new RequestResultManager(); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { requestResultManager.onActivityResult(requestCode, resultCode, data); } @Override List<BaseSettingItem> createList() { List<BaseSettingItem> list = new ArrayList<>(); { ProfileSettingItem item = new ProfileSettingItem(PreferenceEntity.obtain().getProfile()); item.setId(ID_PROFILE); item.setConsumer((obj) -> { RequestProfileDelegate delegate = new RequestProfileDelegate(this, (data) -> onProfileChanged()); requestResultManager.request(delegate); }); list.add(item); } { ExplainSettingItem item = new ExplainSettingItem(""); list.add(item); } { BaseSettingItem item = new BaseSettingItem("关于神马笔记"); item.setIcon(R.drawable.ic_info_outline_white_24dp); item.setIconTintList(getActivity(), R.color.colorIcon); item.setConsumer((obj) -> { String url = "http://andnext.club/whatsnote/help.html"; String args = "ts=" + System.currentTimeMillis(); url = url + "?" + args; PackageUtils.startBrowser(getActivity(), Uri.parse(url)); }); list.add(item); } return list; } @Override void buildAdapter(BridgeAdapter adapter) { adapter.bind(ExplainSettingItem.class, new BridgeBuilder(ExplainSettingViewHolder.class, ExplainSettingViewHolder.LAYOUT_RESOURCE_ID)); adapter.bind(ProfileSettingItem.class, new BridgeBuilder(ProfileSettingViewHolder.class, ProfileSettingViewHolder.LAYOUT_RESOURCE_ID)); adapter.bind(BaseSettingItem.class, new BridgeBuilder(BaseSettingViewHolder.class, BaseSettingViewHolder.LAYOUT_RES_ID)); } }
笔名设置界面,该界面比较复杂。
List<BaseSettingItem> createList()
添加了9个设置项。
void buildAdapter(BridgeAdapter adapter)
绑定了5种ViewHolder。
public class ProfileFragment extends BasePreferenceFragment { static final String ID_PROFILE = "profile"; static final String ID_VISION = "vision"; RequestResultManager requestResultManager; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.requestResultManager = new RequestResultManager(); } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); { this.setTitle("笔名"); } { dividerDecoration.setDrawTop(false); } } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { requestResultManager.onActivityResult(requestCode, resultCode, data); } @Override List<BaseSettingItem> createList() { List<BaseSettingItem> list = new ArrayList<>(); { ProfileSettingItem item = new ProfileSettingItem(PreferenceEntity.obtain().getProfile()); item.setId(ID_PROFILE); item.setDividerVisible(false); item.setConsumer((obj) -> requestPicture((file, sourceUri) -> { ProfileEntity entity = PreferenceEntity.obtain().getProfile(); Uri targetUri = entity.nextPortraitUri(); RequestCropDelegate delegate = new RequestCropDelegate(this, file, sourceUri, targetUri, (uCrop) -> { uCrop.withAspectRatio(1, 1); UCrop.Options options = new UCrop.Options(); options.setCircleDimmedLayer(true); options.setHideBottomControls(true); options.setCompressionFormat(Bitmap.CompressFormat.JPEG); options.setCompressionQuality(60); options.setShowCropGrid(false); options.setShowCropFrame(false); uCrop.withOptions(options); }, (uri) -> setPortrait(uri)); requestResultManager.request(delegate); })); list.add(item); } { ExplainSettingItem item = new ExplainSettingItem(""); list.add(item); } { BaseSettingItem item = new BaseSettingItem("昵称"); item.setDividerType(BaseSettingItem.DIVIDER_NAME); item.setChevron(true); item.setConsumer((obj) -> { RequestNameDelegate delegate = new RequestNameDelegate(this, (name) -> this.setName(name)); requestResultManager.request(delegate); }); list.add(item); } { BaseSettingItem item = new BaseSettingItem("个性签名"); item.setDividerType(BaseSettingItem.DIVIDER_NONE); item.setChevron(true); item.setConsumer((obj) -> { RequestSignatureDelegate delegate = new RequestSignatureDelegate(this, (signature) -> this.setSignature(signature)); requestResultManager.request(delegate); }); list.add(item); } { ExplainSettingItem item = new ExplainSettingItem(""); item.setDividerVisible(false); list.add(item); } { TitleSettingItem item = new TitleSettingItem("个性图签"); list.add(item); } { BaseSettingItem item = new BaseSettingItem("选取新的图片签名"); item.setDividerType(BaseSettingItem.DIVIDER_NAME); item.setChevron(false); item.setConsumer((obj) -> requestPicture((file, sourceUri) -> { ProfileEntity entity = PreferenceEntity.obtain().getProfile(); Uri targetUri = entity.nextVisionUri(); RequestCropDelegate delegate = new RequestCropDelegate(this, file, sourceUri, targetUri, (uCrop) -> { uCrop.withAspectRatio(1.f * ProfileEntity.VISION_WIDTH / ProfileEntity.VISION_HEIGHT, 1); UCrop.Options options = new UCrop.Options(); options.setCircleDimmedLayer(false); options.setHideBottomControls(true); options.setCompressionFormat(Bitmap.CompressFormat.JPEG); options.setCompressionQuality(60); options.setShowCropGrid(false); options.setShowCropFrame(false); uCrop.withOptions(options); }, (uri) -> setVision(uri)); requestResultManager.request(delegate); })); list.add(item); } { PictureSettingItem item = new PictureSettingItem( PreferenceEntity.obtain().getProfile().getVisionUri(), ProfileEntity.VISION_WIDTH, ProfileEntity.VISION_HEIGHT); item.setId(ID_VISION); item.setErrorResId(R.drawable.ic_profile_vision); list.add(item); } { ExplainSettingItem item = new ExplainSettingItem("以图片方式分享笔记时,将显示图片签名。"); item.setDividerVisible(false); list.add(item); } return list; } @Override void buildAdapter(BridgeAdapter adapter) { adapter.bind(TitleSettingItem.class, new BridgeBuilder(TitleSettingViewHolder.class, TitleSettingViewHolder.LAYOUT_RESOURCE_ID)); adapter.bind(ExplainSettingItem.class, new BridgeBuilder(ExplainSettingViewHolder.class, ExplainSettingViewHolder.LAYOUT_RESOURCE_ID)); adapter.bind(ProfileSettingItem.class, new BridgeBuilder(MasterSettingViewHolder.class, MasterSettingViewHolder.LAYOUT_RESOURCE_ID)); adapter.bind(BaseSettingItem.class, new BridgeBuilder(BaseSettingViewHolder.class, BaseSettingViewHolder.LAYOUT_RES_ID)); adapter.bind(PictureSettingItem.class, new BridgeBuilder(PictureSettingViewHolder.class, PictureSettingViewHolder.LAYOUT_RES_ID)); } }
~荒城临古渡~落日满秋山~