实现市面上常见app的用户协议对话框的效果:
- 该对话框无法关闭
- 显示协议文本
- 文本中的超链接跳转到指定url
- 点不同意关闭app
- 点同意继续下一步
- 偏好设置:如果同意了下次来就不会显示对话框
对话框UI构建
几个点注意:
- 这个类是继承了我们自己封装的
BaseDialogFragment
(这个在Fragment通用控制器封装有详细介绍)
- Fragment 获取实例不能直接new,而是去 newInstance 方法里面构造。
codes:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup;
import androidx.fragment.app.FragmentManager;
import com.example.testandroid.R; import com.example.testandroid.fragment.BaseDialogFragment; import com.example.testandroid.superutils.ScreenUtil;
public class TermServiceDialogFragment extends BaseDialogFragment {
@Override public void onResume() { super.onResume(); ViewGroup.LayoutParams params = getDialog().getWindow().getAttributes();
params.width = (int) (ScreenUtil.getScreenWith(getContext()) * 0.9); params.height = ViewGroup.LayoutParams.WRAP_CONTENT; getDialog().getWindow().setAttributes((android.view.WindowManager.LayoutParams) params); }
public static void show(FragmentManager fragmentManager, View.OnClickListener onAgreementClickListener) { TermServiceDialogFragment fragment = newInstance(); fragment.show(fragmentManager,"TermServiceDialogFragment"); }
@Override protected View getLayoutView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_dialog_term_service,container,false); } public static TermServiceDialogFragment newInstance() {
Bundle args = new Bundle();
TermServiceDialogFragment fragment = new TermServiceDialogFragment(); fragment.setArguments(args); return fragment; } }
|
用到的时候展示
1
| TermServiceDialogFragment.show(getSupportFragmentManager(),null);
|
对话框逻辑处理
- html处理&超链接
- 不能关闭
- 不同意按钮
- 同意回调外层界面
html处理
1 2 3 4 5 6 7
| protected void initDatum() { super.initDatum(); Spanned content = Html.fromHtml(getString(R.string.term_service_privacy_content));
SpannableStringBuilder result = SuperTextUtil.setHtmlLinkClick(content,null); contentView.setText(result); }
|
这里的 SuperTextUtil 类是这样的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| package com.example.testandroid.superutils.text;
import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.method.LinkMovementMethod; import android.text.style.URLSpan; import android.view.View; import android.widget.TextView;
import androidx.annotation.NonNull;
public class SuperTextUtil {
public static SpannableStringBuilder setHtmlLinkClick(Spanned data, OnLinkClickListener listener) { SpannableStringBuilder sb = new SpannableStringBuilder(data); URLSpan[] spans = sb.getSpans(0, sb.length(), URLSpan.class);
for (URLSpan span : spans) { int start = sb.getSpanStart(span); int end = sb.getSpanEnd(span); int flags = sb.getSpanFlags(span);
sb.setSpan(new SuperClickableSpan() { @Override public void onClick(@NonNull View widget) { listener.onLinkClick(span.getURL()); } }, start, end, flags); }
return sb; }
public static void setLinkColor(TextView view, int color) { view.setMovementMethod(LinkMovementMethod.getInstance());
view.setLinkTextColor(color);
}
public interface OnLinkClickListener { void onLinkClick(String data); } }
|
上面只是显示html文本。
下面是这段SuperClickableSpan
里面是让超链接那个地方,去掉下划线。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| package com.example.testandroid.superutils.text;
import android.text.TextPaint; import android.text.style.ClickableSpan;
import androidx.annotation.NonNull;
public abstract class SuperClickableSpan extends ClickableSpan {
@Override public void updateDrawState(@NonNull TextPaint ds) {
ds.setColor(ds.linkColor);
ds.setUnderlineText(false); }
}
|
到这里,content文字就可以正常显示了。
且点击就会用系统默认浏览器打开 content 里面的超链接。
然后颜色设置为灰色:
1
| SuperTextUtil.setLinkColor(contentView,getActivity().getColor(R.color.link));
|
这句放在 Fragment 的 initView 里面
到这里,content就完全处理好了
不能关闭
指的是在这个对话框弹出来的时候,点击对话框外or按返回键对话框会关闭。
1 2 3 4 5 6 7 8 9 10
| public class TermServiceDialogFragment extends BaseDialogFragment { @Override protected void initViews() { super.initViews();
setCancelable(false);
} }
|
不同意按钮
在 fragment 里面
1 2 3 4 5 6 7 8 9 10 11 12 13
| protected void initListeners() { super.initListeners();
disagree.setOnClickListener(view -> { dismiss();
SuperProcessUtil.killApp(); }); }
|
这个杀死app是一个工具类:
1 2 3 4 5 6 7 8 9 10 11
|
public class SuperProcessUtil {
public static void killApp() { Process.killProcess(Process.myPid()); } }
|
同意按钮
这个如果同意了,结果想回调到外面去处理。
所以外面是这样的:
直接调用 fragment 里面的 show 方法。传一个监听器进去。
这个监听器在 fragment 里面被保存起来了。
然后我们在 fragment 的initListeners
1 2 3 4 5 6 7 8 9
| primary.setOnClickListener(view -> { dismiss();
onAgreementClickListener.onClick(view);
});
|
直接丢外面去了。
所以整个的一个流程就是:
- 外面在展示dialogfragment的时候就写一个监听器。
- fragment直接保存为类变量
- 在同意按钮被点的时候,调用这个监听器的onClick方法
- 这个onClick方法在外面实现的
这样就实现了,结果在外面监听。
本质就是传了一个监听器,然后合适的地方去调用监听器的onClick回到外面
阶段性成果
偏好设置
也就是说:
第一次来就要同意这个,如果已经同意了就不用再提示了。
工具类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
| import android.content.Context; import android.content.SharedPreferences; import android.preference.PreferenceManager;
public class DefaultPreferenceUtil {
private static final String KEY_MOBILE_NETWORK_PLAY = "mobile_network_play";
private static final String TERMS_SERVICE = "TERMS_SERVICE";
private static DefaultPreferenceUtil instance; private final Context context; private final SharedPreferences preference;
public DefaultPreferenceUtil(Context context) { this.context = context.getApplicationContext();
preference = PreferenceManager.getDefaultSharedPreferences(this.context);
}
public synchronized static DefaultPreferenceUtil getInstance(Context context) { if (instance == null) { instance = new DefaultPreferenceUtil(context); } return instance; }
public void setAcceptTermsServiceAgreement() { putBoolean(TERMS_SERVICE, true); }
public boolean isAcceptTermsServiceAgreement() { return preference.getBoolean(TERMS_SERVICE, false); }
private void putBoolean(String key, boolean value) { preference.edit().putBoolean(key, value).apply(); }
}
|
业务处理
在SplashActivity界面
1 2 3 4 5 6 7 8 9 10 11 12
| @Override protected void initDatum() { super.initDatum();
if (DefaultPreferenceUtil.getInstance(getHostActivity()).isAcceptTermsServiceAgreement()){ prepareNext(); }else { showTermsServiceAgreementDialog(); } }
|
同时记得如果,同意按钮
点击了,记得标记:
1 2 3 4 5 6 7 8 9
|
private void showTermsServiceAgreementDialog() { TermServiceDialogFragment.show(getSupportFragmentManager(), view -> { DefaultPreferenceUtil.getInstance(getHostActivity()).setAcceptTermsServiceAgreement(); prepareNext(); });
}
|
实现结果