Android 如何保证App切换到后台,或页面跳转后,重新打开APP、或返回之前页面时,维持其状态不变

本文介绍了两种Android开发中保持页面状态的方法:一是通过设置启动模式为singleInstance来维持App返回时的页面;二是通过调整launchMode为singleTask或singleInstance防止页面在跳转前后状态改变。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

项目中遇到的一些小问题,记录、分享一下。

Android 如何保证App切换到后台,或页面跳转后,重新打开APP、或返回之前页面时,维持其页面状态不变?


1、问:当APP启动后,打开某一界面,然后点击手机HOME键,使应用程序退到后台;当再次打开App时,如何保证App当前显示页面还是刚才退出时的页面?

     答:在 Manifest.xml 中 application 标签内 设置启动模式 为 singleInstance ,即可。

android:launchMode="singleInstance"


===========================**********************==================================

2、问:从第一页跳转到第二页,然后再从第二页跳回第一页后,如何保证第一页面还是跳转前的状态?

     答:在第一页写 Intent 跳转时,不要销毁此页;且 在 Manifest.xml 里 给第一页的 <activity>标签 加 launchMode 为

singleTask 或  SingleInstance;

android:launchMode="singleTask"

android:launchMode="singleInstance"





package com.lc.bailingbird; import static com.lc.bailingbird.base.BaseApplication.Blistener; import static com.lc.bailingbird.base.BaseApplication.bluetoothSDK; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.graphics.Color; import android.net.Uri; import android.os.Bundle; import android.text.TextUtils; import android.util.Log; import android.view.KeyEvent; import android.view.View; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentResultListener; import androidx.viewpager2.adapter.FragmentStateAdapter; import androidx.viewpager2.widget.ViewPager2; import com.bes_ota.bluetoothsdk.BluetoothSDK; import com.google.android.material.badge.BadgeDrawable; import com.lc.bailingbird.base.BaseActivity; import com.lc.bailingbird.base.BaseApplication; import com.lc.bailingbird.conn.BaseAsyCallBack; import com.lc.bailingbird.conn.CartListPost; import com.lc.bailingbird.databinding.ActivityMainBinding; import com.lc.bailingbird.dialog.CommonDialog; import com.lc.bailingbird.fragment.ChatFragment; import com.lc.bailingbird.fragment.HomeFragmentNew; import com.lc.bailingbird.fragment.MarketFragment; import com.lc.bailingbird.fragment.MineFragment; import com.lc.bailingbird.fragment.MyFragment; import com.lc.bailingbird.fragment.NewsFragment; import com.lc.bailingbird.home.GoodDetailActivity; import com.lc.bailingbird.home.NewcomerGuideActivity; import com.lc.bailingbird.home.WebActivity; import com.lc.bailingbird.home.launcherActivity; import com.lc.bailingbird.login.ForgetPWDActivity; import com.lc.bailingbird.login.LoginActivity; import com.lc.bailingbird.login.RegisterActivity; import com.lc.bailingbird.mine.WelcomeActivity; import com.lc.bailingbird.util.BluetoothUtil; import com.lc.bailingbird.util.GlideImageLoader; import com.lc.bailingbird.util.SPManager; import com.mob.MobSDK; import com.petterp.floatingx.FloatingX; import com.petterp.floatingx.assist.FxGravity; import com.petterp.floatingx.assist.helper.AppHelper; import com.petterp.floatingx.impl.lifecycle.FxTagActivityLifecycleImpl; import com.qiyukf.nimlib.sdk.StatusBarNotificationConfig; import com.qiyukf.unicorn.api.ConsultSource; import com.qiyukf.unicorn.api.OnMessageItemClickListener; import com.qiyukf.unicorn.api.Unicorn; import com.qiyukf.unicorn.api.YSFOptions; import com.qiyukf.unicorn.ui.activity.LeaveMessageActivity; import com.qiyukf.unicorn.ui.activity.ServiceMessageActivity; import com.tencent.bugly.crashreport.CrashReport; import com.zcx.helper.view.toast.ToastUtils; public class MainActivity extends BaseActivity<ActivityMainBinding> { FragmentStateAdapter adapter; // boolean ISCLICK = true; // CommonDialog dialog; @Override protected void iniClick() { getSupportFragmentManager().setFragmentResultListener("position", this, new FragmentResultListener() { @Override public void onFragmentResult(@NonNull String requestKey, @NonNull Bundle result) { Log.e("HOME_TAG", "滑动?" + result.getInt("position")); binding.viewpager.setCurrentItem(result.getInt("position"), true); if (result.getInt("position") == 1 && result.containsKey("title")) { getSupportFragmentManager().setFragmentResult("classTitle", result); } } }); } @Override public void iniView() { super.iniView(); // dialog = new CommonDialog(MainActivity.this, "提示", "客服功能需要麦克风权限"); binding.viewpager.setAdapter(adapter = new FragmentStateAdapter(this) { @NonNull @Override public Fragment createFragment(int position) { switch (position) { case 0: return new NewsFragment(); case 1: return new MarketFragment(); case 2: return new HomeFragmentNew(); case 3: return new ChatFragment(); default: return new MineFragment(); } } @Override public int getItemCount() { return 5; } }); binding.viewpager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() { @Override public void onPageSelected(int position) { super.onPageSelected(position); switch (position) { case 0: binding.tab.setSelectedItemId(R.id.menu_news); break; case 1: binding.tab.setSelectedItemId(R.id.menu_market); break; case 2: binding.tab.setSelectedItemId(R.id.menu_home); break; case 3: binding.tab.setSelectedItemId(R.id.menu_message); break; case 4: binding.tab.setSelectedItemId(R.id.menu_my); break; } } }); // binding.tab.setSelectedItemId(R.id.menu_home); binding.tab.setOnItemSelectedListener(item -> { int itemId = item.getItemId(); if (itemId == R.id.menu_news) { binding.viewpager.setCurrentItem(0, true); return true; } else if (itemId == R.id.menu_market) { binding.viewpager.setCurrentItem(1, true); return true; } else if (itemId == R.id.menu_home) { binding.viewpager.setCurrentItem(2, true); return true; } else if (itemId == R.id.menu_message) { if (!SPManager.get().getLoginStatus()) { LoginActivity.start(this); return false; } binding.viewpager.setCurrentItem(3, true); return true; } else if (itemId == R.id.menu_my) { if (!SPManager.get().getLoginStatus()) { LoginActivity.start(this); return false; } binding.viewpager.setCurrentItem(4, true); return true; } return false; }); } @Override protected void onDestroy() { super.onDestroy(); // EventBus.getDefault().unregister(this); } public void setPrivate() { MobSDK.submitPolicyGrantResult(true); CrashReport.initCrashReport(getApplicationContext(), "2a231036be", false); } @Override protected void onResume() { super.onResume(); getCartNum(); } public void getCartNum() { new CartListPost(new BaseAsyCallBack<CartListPost.RespBean>(MainActivity.this) { @Override protected void onStart() { super.onStart(); } @Override protected void onSuccess(String toast, CartListPost.RespBean bean) { BadgeDrawable badge = binding.tab.getOrCreateBadge(R.id.menu_message); if (bean.getResult().isEmpty()) { binding.tab.removeBadge(R.id.menu_message); } else { if (bean.getResult().size() > 0) { badge.setVisible(true); badge.setVerticalOffset(10); badge.setNumber(bean.getResult().get(0).getList().size()); badge.setBackgroundColor(Color.parseColor("#F30B14")); badge.setBadgeTextColor(Color.parseColor("#FFFFFF")); badge.setMaxCharacterCount(99); } else { binding.tab.removeBadge(R.id.menu_message); } } } @Override protected void onFail(String toast) { ToastUtils.show(toast); } @Override protected void onEnd(String toast) { super.onEnd(toast); } }).execute(false); } private long mExitTime; @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { if ((System.currentTimeMillis() - mExitTime) > 2000) { ToastUtils.show("再按一次退出APP"); mExitTime = System.currentTimeMillis(); } else { //后台运行 不结束程序 只是退出到后台 Intent intent = new Intent(); intent.setAction("android.intent.action.MAIN"); intent.addCategory("android.intent.category.HOME"); startActivity(intent); //System.exit(0); //常规java、c#的标准退出法,返回值为0代表正常退出 结束程序 } return true; } return super.onKeyDown(keyCode, event); } } 怎么在ViewPager滑动的候添加是否登录验证
最新发布
07-10
<think>我们正在处理一个Android开发问题,用户想在ViewPager2的滑动事件中添加登录验证逻辑,以防止未登录用户查看特定页面。 思路: 1. 我们需要监听ViewPager2的页面切换事件。 2. 在切换事件中,判断目标页面是否是受保护的页面(即需要登录才能查看)。 3. 如果目标页面需要登录,而用户未登录,则拦截滑动,并跳转到登录页面。 实现步骤: a. 使用ViewPager2的registerOnPageChangeCallback方法注册一个页面切换回调。 b. 在回调的onPageSelected方法中,我们可以知道将要显示的页面位置。 c. 根据位置判断该页面是否需要登录验证。 d. 如果需要验证且未登录,则触发登录流程,并阻止用户进入该页面(可以将ViewPager2设置回原来的位置)。 但是注意:ViewPager2的滑动事件已经发生,页面已经切换,所以我们不能直接在onPageSelected中阻止滑动。因此,我们需要在滑动开始前进行判断。 替代方案: 我们可以使用ViewPager2的setUserInputEnabled方法来控制用户是否可以手动滑动。但是,这并不能在滑动过程中动态拦截。 更好的方案:在页面切换之前进行拦截。我们可以通过重写PagerAdapter的setPrimaryItem方法?但是ViewPager2的内部机制不同,它使用RecyclerView.Adapter。 实际上,ViewPager2提供了fakeDragBy方法来实现模拟拖拽,但这并不适合。 另一种思路:在滑动过程中,我们可以通过监听滑动状态,在滑动开始前(onPageScrollStateChanged)进行判断,但同样,我们无法在滑动开始前获取目标页面。 因此,我们可以结合使用: 1. 在onPageScrollStateChanged中,当状态为SCROLL_STATE_DRAGGING(开始拖动),我们可以通过ViewPager2的getCurrentItem获取当前页面,然后通过触摸事件获取滑动的方向,从而预测下一个页面?这样比较复杂,而且不准确。 2. 另一种更常见的做法:在用户滑动到需要登录的页面,我们立即跳转到登录页面,并在登录成功后回到原页面,同将ViewPager2设置回上一个页面留在当前页面,但需要记录状态)。 但是,用户可能会看到需要登录的页面一闪而过,然后才被跳转,体验不好。 3. 使用ViewPager2的PageTransformer?它主要用于动画效果,不适合做逻辑拦截。 4. 使用ViewPager2的beginFakeDrag()和fakeDragBy()来模拟滑动?这通常用于程序控制滑动,而不是用户交互。 5. 自定义ViewPager2:重写onInterceptTouchEvent和onTouchEvent方法,在触摸事件发生判断用户是否要滑动到需要登录的页面,如果是,则拦截事件并跳转登录。但这样需要计算滑动方向和目标页面。 考虑到复杂度,我们可以采用以下折中方案: - 在onPageSelected回调中,如果发现目标页面需要登录而用户未登录,则: a. 记录当前页面的位置(即跳转前的页面位置) b. 跳转到登录页面 c. 登录成功后,将ViewPager2设置回之前记录的位置(者根据业务需求设置到其他位置) 但是,这样用户会先看到目标页面,然后被跳转到登录页面,体验不佳。 改进:在onPageSelected中立即将页面切换回原来的位置?但是这样会导致页面闪动。 更优的解决方案:在滑动过程中,当状态变为SCROLL_STATE_SETTLING(正在定位到目标页面,我们可以检查目标页面是否需要登录,如果需要登录且未登录,则取消定位,并立即将页面设置回原来的位置,同跳转登录。 具体步骤: 1. 注册ViewPager2的OnPageChangeCallback。 2. 在回调中,我们需要记录上一次的页面位置(currentPosition)和当前的滚动状态。 3. 当滚动状态变为SCROLL_STATE_SETTLING,我们获取目标页面(即onPageSelected中传入的位置,但我们还没有在onPageSelected中处理,所以可以在这里先判断)。 注意:onPageSelected是在页面已经切换完成后才调用的?实际上,在滚动状态变化中,顺序是: SCROLL_STATE_DRAGGING -> 然后会调用onPageScrolled -> 然后当滑动完成状态变为SCROLL_STATE_SETTLING,然后变为SCROLL_STATE_IDLE,同调用onPageSelected。 实际上,onPageSelected是在页面被选中(即切换完成)调用的。所以我们在状态变为SCROLL_STATE_SETTLING,已经知道目标页面(因为此已经确定了目标页面)。 但是,在SCROLL_STATE_SETTLING状态,我们还没有调用onPageSelected,所以我们可以通过ViewPager2的getCurrentItem()获取当前页面(但注意,此当前页面还是原来的页面,目标页面还没有设置)。所以我们需要在状态为SCROLL_STATE_DRAGGING记录开始位置,然后通过计算手势方向来预测目标页面?这比较复杂。 另一种做法:在onPageScrolled方法中,我们可以得到当前页面和偏移量,从而计算出目标页面。但同样复杂。 考虑到这些复杂性,我们可能更倾向于使用一个更简单但有效的方案:在onPageSelected中处理,虽然会有一瞬间的页面显示,但我们可以通过让需要登录的页面内容初始化为空白加载中等方式避免用户看到内容,然后立即跳转登录。 具体实现(在Fragment中): 假设我们的ViewPager2包含多个Fragment,其中某些Fragment需要登录。 步骤: 1. 在ViewPager2的OnPageChangeCallback中,重写onPageSelected方法。 2. 在onPageSelected中,根据位置判断该页面是否需要登录(可以预先定义一个列表,者根据Fragment类型判断)。 3. 如果需要登录且未登录,则: // 记录当前要跳转的位置(即需要登录的页面位置),因为马上我们要跳走,登录回来需要知道要回到哪个页面 // 跳转到登录Activity // 同,将ViewPager2的当前页面设置回上一个页面(因为此已经跳到了需要登录的页面,我们需要回退) viewPager2.setCurrentItem(previousPage, false); // 立即跳转,不使用滚动动画 4. 但是,这样会有一个问题:用户会先看到需要登录的页面,然后才跳转,而且页面会闪动。 为了避免用户看到内容,我们可以在该Fragment的onResume中检查登录状态,如果没有登录,则启动登录Activity,并且立即finish当前Activity?这取决于我们的页面结构。 如果我们的ViewPager2是在一个Activity中,那么我们可以这样处理:在Fragment中,当它变为可见(比如onResume),检查是否需要登录,如果需要且未登录,则启动登录Activity,并关闭自己?但是Fragment不能关闭Activity,只能由Activity来做。 因此,我们可以让Activity监听这些需要登录的Fragment,当它们变为可见,进行登录检查。我们可以通过ViewPager2的当前页面来在Activity中做这个检查。 所以,我们回到在Activity中处理ViewPager2的页面切换事件。 综合以上,我们采用以下方案: 在Activity中: 1. 定义一个变量lastPage,用于记录上一次的页面位置(在onPageSelected中每次更新)。 2. 在onPageSelected中,当切换到新页面,判断该页面(position)是否需要登录。 3. 如果需要登录且未登录,则: // 保存目标页面的位置(因为登录成功后可能需要跳转到这个页面) // 跳转到登录Activity // 同,将ViewPager2设置回lastPage(即上一个页面) viewPager2.setCurrentItem(lastPage, false); 4. 然后启动登录Activity。 注意:lastPage需要在每次成功切换页面后更新(除了被拦截的情况)。在拦截的情况下,我们不更新lastPage,因为页面实际上没有切换成功(我们立即设置回去了)。 但是,在跳转登录之前页面已经切换到了目标页面,然后我们立即设置回上一个页面,用户可能会看到快速闪动。 为了避免闪动,我们可以使用一个技巧:在需要登录的Fragment的布局中,默认设置为不可见(者用一个覆盖层),然后在Fragment中检查登录状态,如果已经登录,则显示内容,否则显示登录提示。同,在Activity中,我们仍然做上述拦截,这样双重保障。这样即使闪动,用户看到的也是空白登录提示。 另一种避免闪动的方法:在ViewPager2的滑动过程中,我们通过OnPageChangeCallback的onPageScrollStateChanged方法,在状态变为SCROLL_STATE_DRAGGING,记录当前页面(currentPage),然后通过触摸事件判断滑动方向,预测目标页面,然后判断目标页面是否需要登录。如果需要登录且未登录,则调用viewPager2.setUserInputEnabled(false)来禁止滑动,然后跳转登录,登录成功后重新启用滑动。但是,预测目标页面比较麻烦,因为用户可能滑动一半又滑回来。 考虑到间,我们采用第一种方案(双重保障): 方案一(主方案):在Activity中,通过OnPageChangeCallback拦截,并回退页面,同跳转登录。 方案二(辅助):在需要登录的Fragment中,检查登录状态,如果未登录,则显示登录提示,并隐藏内容。 下面我们重点实现方案一。 代码结构: 1. 在Activity中初始化ViewPager2,并注册回调: ```java private ViewPager2 viewPager2; private int lastPage = 0; // 记录上一次的页面位置 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); viewPager2 = findViewById(R.id.viewPager); // 设置Adapter(这里假设已经设置好了) viewPager2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() { @Override public void onPageSelected(int position) { super.onPageSelected(position); // 判断该位置是否需要登录 if (isPageRequiresLogin(position) && !isUserLoggedIn()) { // 保存目标页面,用于登录成功后跳转 int targetPage = position; // 这个页面就是需要登录的页面 // 跳转到登录页面 startLoginActivity(targetPage); // 回退到上一个页面 viewPager2.setCurrentItem(lastPage, false); } else { // 更新lastPage lastPage = position; } } }); } private boolean isPageRequiresLogin(int position) { // 根据位置判断,比如位置1和2需要登录 return position == 1 || position == 2; } private boolean isUserLoggedIn() { // 检查登录状态 // 返回true表示已登录,false表示未登录 return false; } private void startLoginActivity(int targetPage) { // 启动登录Activity,并传递targetPage,以便登录成功后回到这个页面 Intent intent = new Intent(this, LoginActivity.class); intent.putExtra("targetPage", targetPage); startActivity(intent); } ``` 2. 在登录成功后,我们回到这个Activity,并且需要跳转到目标页面(如果登录成功): 在Activity的onResume中,我们可以检查登录状态,如果已经登录,并且之前有保存的目标页面,则跳转过去。 但是注意,我们之前没有保存目标页面,而是通过启动登录Activity传递了targetPage。登录Activity在登录成功后,应该返回结果,告诉我们哪个页面需要跳转。 因此,我们使用startActivityForResult(者Activity Result API)来启动登录Activity。 修改: ```java // 定义一个请求码 private static final int LOGIN_REQUEST = 1001; private void startLoginActivity(int targetPage) { Intent intent = new Intent(this, LoginActivity.class); intent.putExtra("targetPage", targetPage); startActivityForResult(intent, LOGIN_REQUEST); } @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == LOGIN_REQUEST) { if (resultCode == RESULT_OK) { // 登录成功,获取目标页面 int targetPage = data.getIntExtra("targetPage", 0); // 跳转到目标页面 viewPager2.setCurrentItem(targetPage, true); // 更新lastPage lastPage = targetPage; } } } ``` 在登录Activity中,登录成功后: ```java // 登录成功 Intent resultIntent = new Intent(); resultIntent.putExtra("targetPage", getIntent().getIntExtra("targetPage", 0)); setResult(RESULT_OK, resultIntent); finish(); ``` 3. 注意:在onPageSelected中,当我们回退页面,会再次触发onPageSelected,此position为lastPage。由于lastPage不需要登录(者已经登录),所以不会再次触发登录。但是,我们需要避免在回退更新lastPage,因为我们在回退后仍然希望lastPage是上一次的页面(即回退后的页面就是lastPage,不需要更新)。实际上,在回退操作中,我们调用了setCurrentItem(lastPage, false),这会再次触发onPageSelected,参数为lastPage。在onPageSelected中,由于条件不满足(不需要登录者已经登录),会走else分支,将lastPage更新为lastPage(即不变)。所以没有问题。 但是,注意:在回退操作中,我们首先触发了onPageSelected(position)(目标页面),然后我们立即回退,又触发了onPageSelected(lastPage)。因此,在第一次触发(目标页面,我们启动了登录Activity并回退了页面,然后第二次触发(lastPage),我们更新lastPage为lastPage(即不变)。所以lastPage的值没有变化,这是正确的。 但是,在第一次触发,lastPage还是上一次的页面,在回退后,第二次触发,lastPage被更新为lastPage(自己),所以实际上lastPage没有变化。这样,如果用户再次滑动到需要登录的页面,我们仍然可以回退到原来的lastPage。 4. 双重保障:在需要登录的Fragment中,我们也要检查登录状态。如果未登录,则显示登录提示,隐藏内容;如果已登录,则显示内容。 在Fragment的onResume中: ```java @Override public void onResume() { super.onResume(); if (!isUserLoggedIn()) { // 显示登录提示,隐藏内容 showLoginPrompt(); } else { // 显示内容 showContent(); } } ``` 这样,即使Activity中的拦截因为某种原因没有生效(比如快速滑动),用户看到的也不是内容,而是登录提示。 总结:我们通过Activity中ViewPager2的页面切换回调进行主要拦截,并在Fragment中进行内容隐藏作为辅助,实现了未登录用户无法查看特定页面的功能。 注意:由于在拦截我们立即回退了页面,所以用户可能会感觉到页面滑动被取消,但不会看到敏感内容。 相关问题: 1. 如何在ViewPager2中动态更新Fragment? 2. ViewPager2和ViewPager有什么区别? 3. 如何实现ViewPager2的页面预加载控制?</think>### 在 ViewPager2 中添加滑动登录验证的实现方案 在 ViewPager2 中添加用户登录验证逻辑,可以通过监听页面切换事件并拦截未授权访问来实现。以下是完整实现步骤: #### 1. 创建登录验证回调接口 ```java public interface AuthInterceptor { boolean requireAuth(int position); // 检查指定位置是否需要登录 void onAuthRequired(); // 触发登录流程 } ``` #### 2. 实现 ViewPager2 滑动拦截器 ```java public class AuthPageChangeCallback extends ViewPager2.OnPageChangeCallback { private final ViewPager2 viewPager; private final AuthInterceptor authInterceptor; private int lastValidPosition = 0; public AuthPageChangeCallback(ViewPager2 viewPager, AuthInterceptor interceptor) { this.viewPager = viewPager; this.authInterceptor = interceptor; } @Override public void onPageSelected(int position) { if (authInterceptor.requireAuth(position) && !isUserLoggedIn()) { // 拦截未授权访问 viewPager.post(() -> viewPager.setCurrentItem(lastValidPosition, false)); authInterceptor.onAuthRequired(); } else { lastValidPosition = position; // 更新合法位置 } } } ``` #### 3. 在 Activity/Fragment 中集成验证 ```java public class MainActivity extends AppCompatActivity implements AuthInterceptor { private ViewPager2 viewPager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); viewPager = findViewById(R.id.view_pager); viewPager.setAdapter(new MyPagerAdapter(this)); // 注册验证回调 viewPager.registerOnPageChangeCallback( new AuthPageChangeCallback(viewPager, this) ); } @Override public boolean requireAuth(int position) { // 定义需要登录的页面索引(例如第2/3页) return position == 1 || position == 2; } @Override public void onAuthRequired() { startActivity(new Intent(this, LoginActivity.class)); Toast.makeText(this, "请先登录", Toast.LENGTH_SHORT).show(); } private boolean isUserLoggedIn() { // 实现实际的登录状态检查逻辑 return SharedPreferencesHelper.getLoginStatus(); } } ``` #### 4. 在 Fragment 中添加双重验证(可选) ```java public class SecureFragment extends Fragment { @Override public void onResume() { super.onResume(); if (requireAuth() && !isUserLoggedIn()) { // 防止绕过滑动拦截直接访问 requireActivity().startActivity(new Intent(requireActivity(), LoginActivity.class)); requireActivity().finish(); } } private boolean requireAuth() { return true; // 标记为需要验证的Fragment } } ``` ### 关键实现细节说明 1. **拦截机** - 在 `onPageSelected` 回调中验证目标页面权限 - 使用 `post()` 确保 UI 线程安全操作 - 立即回退到上一个合法位置 (`lastValidPosition`) 2. **状态管理** - 记录最近的有效页面位置 - 登录成功后自动跳转回目标页面: ```java // 在登录成功后 Intent result = new Intent(); result.putExtra("TARGET_PAGE", targetPosition); setResult(RESULT_OK, result); finish(); ``` 3. **防止视觉闪烁** - `setCurrentItem(lastValidPosition, false)` 禁用切换动画 - 在需要验证的 Fragment 使用占位布局: ```xml <FrameLayout> <View android:visibility="gone"/> <!-- 实际内容 --> <LoginPromptView/> <!-- 未登录显示的提示 --> </FrameLayout> ``` ### 性能优化建议 1. 使用 `FragmentStateAdapter` 替代 `FragmentPagerAdapter`,确保未访问页面不会占用内存[^1] 2. 在 `onAuthRequired()` 中实现登录状态缓存,避免重复检查 3. 对高频滑动事件添加防抖逻辑: ```java private long lastAuthTime = 0; if (System.currentTimeMillis() - lastAuthTime > 1000) { authInterceptor.onAuthRequired(); lastAuthTime = System.currentTimeMillis(); } ``` ### 备选方案:基于自定义行为的实现 ```java public class SecureViewPager2 extends ViewPager2 { // 重写触摸事件拦截 @Override public boolean onInterceptTouchEvent(MotionEvent event) { if (shouldBlockSwipe()) { return false; // 禁用滑动 } return super.onInterceptTouchEvent(event); } private boolean shouldBlockSwipe() { // 根据滑动方向预测目标页面 int target = predictTargetPage(); return requireAuth(target) && !isLoggedIn(); } } ``` > **最佳实践**:推荐使用第一种方案,它符合关注点分离原则且更易维护。双重验证机制可防止用户通过旋转屏幕等操作绕过检查[^1]。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值