欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android?Navigation重建Fragment問題分析及解決

 更新時(shí)間:2022年09月15日 08:46:07   作者:BlueSocks  
這篇文章主要介紹了Android?Navigation重建Fragment問題分析及解決,文章通過圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下

前言

最近項(xiàng)目中使用到了BottomNavigationView結(jié)合Navigation實(shí)現(xiàn)底部導(dǎo)航欄切換頁(yè)面業(yè)務(wù)。

NavigationUI.setupWithNavController(bottomNavigationView, navController);

結(jié)果發(fā)現(xiàn)每次點(diǎn)擊底部導(dǎo)航欄切換的時(shí)候都會(huì)重建Fragment,于是分析了源碼,并研究了解決方案。

源碼分析:

首先看布局文件:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <fragment
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:defaultNavHost="true"
        app:layout_constraintBottom_toTopOf="@+id/bottom_nav_view"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:navGraph="@navigation/nav_graph" />

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/bottom_nav_view"
        android:layout_width="0dp"
        android:layout_height="50dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:menu="@menu/bottom_menu" />
</androidx.constraintlayout.widget.ConstraintLayout>

當(dāng)調(diào)用NavigationUI.setupWithNavController(BottomNavigationView, NavController)以后,setupWithNavController方法內(nèi)部其實(shí)通過調(diào)用BottomNavigationView#setOnNavigationItemSelectedListener方法監(jiān)聽導(dǎo)航欄選中事件。
在BottomNavigationView.OnNavigationItemSelectedListener監(jiān)聽中,最終會(huì)調(diào)用到NavController#navigate方法,進(jìn)入Navigation源碼中。

Navigation源碼分析

首先看NavHostFragment的執(zhí)行流程。

1. NavHostFragment#onInflate

因?yàn)樵趚ml中聲明fragment因此,首先調(diào)用Fragment的onInflate方法。

    @CallSuper
    @Override
    public void onInflate(@NonNull Context context, @NonNull AttributeSet attrs,
            @Nullable Bundle savedInstanceState) {
        super.onInflate(context, attrs, savedInstanceState);

        final TypedArray navHost = context.obtainStyledAttributes(attrs,
                androidx.navigation.R.styleable.NavHost);
        final int graphId = navHost.getResourceId(
                androidx.navigation.R.styleable.NavHost_navGraph, 0);
        if (graphId != 0) {
            mGraphId = graphId;
        }
        navHost.recycle();

        final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.NavHostFragment);
        final boolean defaultHost = a.getBoolean(R.styleable.NavHostFragment_defaultNavHost, false);
        if (defaultHost) {
            mDefaultNavHost = true;
        }
        a.recycle();
    }

onInflate方法中主要是從XML屬性中解析navGraph屬性和defaultNavHost屬性值。

2. NavHostFragment#onAttach

根據(jù)Fragment生命周期,然后執(zhí)行的是onAttach方法。

    @CallSuper
    @Override
    public void onAttach(@NonNull Context context) {
        super.onAttach(context);
        if (mDefaultNavHost) {
            getParentFragmentManager().beginTransaction()
                    .setPrimaryNavigationFragment(this)
                    .commit();
        }
    }

onAttach方法中主要是設(shè)置NavHostFragment為導(dǎo)航器的主導(dǎo)航容器。

3. NavHostFragment#onCreate

 	@CallSuper
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        final Context context = requireContext();

		// 1. 實(shí)例化NavHostController
        mNavController = new NavHostController(context);
        mNavController.setLifecycleOwner(this);
        mNavController.setOnBackPressedDispatcher(requireActivity().getOnBackPressedDispatcher());
        mNavController.enableOnBackPressed(
                mIsPrimaryBeforeOnCreate != null && mIsPrimaryBeforeOnCreate);
        mIsPrimaryBeforeOnCreate = null;
        mNavController.setViewModelStore(getViewModelStore());
        // 2. 創(chuàng)建DialogFragmentNavigator和FragmentNavigator并添加示例到NavigatorProvider中
        onCreateNavController(mNavController);

        Bundle navState = null;
        if (savedInstanceState != null) {
            navState = savedInstanceState.getBundle(KEY_NAV_CONTROLLER_STATE);
            if (savedInstanceState.getBoolean(KEY_DEFAULT_NAV_HOST, false)) {
                mDefaultNavHost = true;
                getParentFragmentManager().beginTransaction()
                        .setPrimaryNavigationFragment(this)
                        .commit();
            }
            mGraphId = savedInstanceState.getInt(KEY_GRAPH_ID);
        }
        if (navState != null) {
            mNavController.restoreState(navState);
        }
        if (mGraphId != 0) {
        	// 3. 設(shè)置導(dǎo)航配置文件
            mNavController.setGraph(mGraphId);
        } else {
            final Bundle args = getArguments();
            final int graphId = args != null ? args.getInt(KEY_GRAPH_ID) : 0;
            final Bundle startDestinationArgs = args != null
                    ? args.getBundle(KEY_START_DESTINATION_ARGS)
                    : null;
            if (graphId != 0) {
                mNavController.setGraph(graphId, startDestinationArgs);
            }
        }
        super.onCreate(savedInstanceState);
    }

onCreate方法中主要做三件事:

  • 實(shí)例化NavHostController對(duì)象
  • 創(chuàng)建DialogFragmentNavigator和FragmentNavigator并添加到NavHostController的父類NavController的NavigatorProvider類型的成員變量mNavigatorProvider中
  • 調(diào)用NavHostController#setGraph方法設(shè)置導(dǎo)航配置文件nav_graph
public class NavHostController extends NavController {

    public NavHostController(@NonNull Context context) {
        super(context);
    }
    ...
}

主要看父類初始化方法:

public class NavController {
	private NavigatorProvider mNavigatorProvider = new NavigatorProvider();
	public NavController(@NonNull Context context) {
       	...
        mNavigatorProvider.addNavigator(new NavGraphNavigator(mNavigatorProvider));
        mNavigatorProvider.addNavigator(new ActivityNavigator(mContext));
    }
}

主要是創(chuàng)建NavGraphNavigator和ActivityNavigator實(shí)例并添加到NavController的成員變量mNavigatorProvider中。

4. NavHostFragment#onCreateNavController

    @CallSuper
    protected void onCreateNavController(@NonNull NavController navController) {
        navController.getNavigatorProvider().addNavigator(
                new DialogFragmentNavigator(requireContext(), getChildFragmentManager()));
        navController.getNavigatorProvider().addNavigator(createFragmentNavigator());
    }

onCreate方法中調(diào)用了onCreateNavController方法添加DialogFragmentNavigator和FragmentNavigator示例。

5. NavigatorProvider

public class NavigatorProvider {
    private static final HashMap<Class<?>, String> sAnnotationNames = new HashMap<>();
	@NonNull
    static String getNameForNavigator(@NonNull Class<? extends Navigator> navigatorClass) {
        String name = sAnnotationNames.get(navigatorClass);
        if (name == null) {
        	// 自定義Navigator類的注解Navigator.Name
            Navigator.Name annotation = navigatorClass.getAnnotation(Navigator.Name.class);
            name = annotation != null ? annotation.value() : null;
            ...
            sAnnotationNames.put(navigatorClass, name);
        }
        return name;
    }
	private final HashMap<String, Navigator<? extends NavDestination>> mNavigators = new HashMap<>()
	@Nullable
    public final Navigator<? extends NavDestination> addNavigator(
            @NonNull Navigator<? extends NavDestination> navigator) {
        String name = getNameForNavigator(navigator.getClass());
        return addNavigator(name, navigator);
    }
    @CallSuper
    @Nullable
    public Navigator<? extends NavDestination> addNavigator(@NonNull String name,
            @NonNull Navigator<? extends NavDestination> navigator) {
        return mNavigators.put(name, navigator);
    }
}

NavigatorProvider類內(nèi)部主要是存儲(chǔ)了鍵值為自定義Navigator時(shí)注解Navigator.Name指定的名稱,值為對(duì)應(yīng)的Navigator示例。

因此onCreate方法執(zhí)行以后,NavigatorProvider中的mNavigators的值為:

("navigation", NavGraphNavigator)
("activity", ActivityNavigator)
("dialog", DialogFragmentNavigator)
("fragment", FragmentNavigator)

6. NavController#setGraph

@CallSuper
public void setGraph(@NavigationRes int graphResId) {
    setGraph(graphResId, null);
}

@CallSuper
public void setGraph(@NavigationRes int graphResId, @Nullable Bundle startDestinationArgs) {
    setGraph(getNavInflater().inflate(graphResId), startDestinationArgs);
}

@CallSuper
public void setGraph(@NonNull NavGraph graph, @Nullable Bundle startDestinationArgs) {
    if (mGraph != null) {
        popBackStackInternal(mGraph.getId(), true);
    }
    mGraph = graph;
    onGraphCreated(startDestinationArgs);
}

@NonNull
public NavInflater getNavInflater() {
    if (mInflater == null) {
        mInflater = new NavInflater(mContext, mNavigatorProvider);
    }
    return mInflater;
}

這個(gè)方法中首先是實(shí)例化NavInflater并調(diào)用NavInflater#inflate解析導(dǎo)航配置文件,解析以后的結(jié)構(gòu)存放在NavGraph類中。NavGraph是可以按ID獲取的NavDestination節(jié)點(diǎn)的樹形結(jié)構(gòu)。

7. NavInflater#inflate

public final class NavInflater {
	private Context mContext;
	private NavigatorProvider mNavigatorProvider;
    public NavInflater(@NonNull Context context, @NonNull NavigatorProvider navigatorProvider) {
        mContext = context;
        mNavigatorProvider = navigatorProvider;
    }
	    @NonNull
    public NavGraph inflate(@NavigationRes int graphResId) {
        ...
        NavDestination destination = inflate(res, parser, attrs, graphResId);
        ...
    }
	 @NonNull
    private NavDestination inflate(@NonNull Resources res, @NonNull XmlResourceParser parser,
            @NonNull AttributeSet attrs, int graphResId)
            throws XmlPullParserException, IOException {
        Navigator<?> navigator = mNavigatorProvider.getNavigator(parser.getName());
        final NavDestination dest = navigator.createDestination();
        dest.onInflate(mContext, attrs);
        final int innerDepth = parser.getDepth() + 1;
        int type;
        int depth;
        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && ((depth = parser.getDepth()) >= innerDepth
                || type != XmlPullParser.END_TAG)) {
            if (type != XmlPullParser.START_TAG) {
                continue;
            }

            if (depth > innerDepth) {
                continue;
            }

            final String name = parser.getName();
            if (TAG_ARGUMENT.equals(name)) {
                inflateArgumentForDestination(res, dest, attrs, graphResId);
            } else if (TAG_DEEP_LINK.equals(name)) {
                inflateDeepLink(res, dest, attrs);
            } else if (TAG_ACTION.equals(name)) {
                inflateAction(res, dest, attrs, parser, graphResId);
            } else if (TAG_INCLUDE.equals(name) && dest instanceof NavGraph) {
                final TypedArray a = res.obtainAttributes(
                        attrs, androidx.navigation.R.styleable.NavInclude);
                final int id = a.getResourceId(
                        androidx.navigation.R.styleable.NavInclude_graph, 0);
                ((NavGraph) dest).addDestination(inflate(id));
                a.recycle();
            } else if (dest instanceof NavGraph) {
                ((NavGraph) dest).addDestination(inflate(res, parser, attrs, graphResId));
            }
        }
        return dest;
    }
	...
}

NavInflater的主要工作就是解析導(dǎo)航配置文件。接下來再回頭看setGraph方法中調(diào)用的onGraphCreated方法。

8. NavController#onGraphCreated

    private void onGraphCreated(@Nullable Bundle startDestinationArgs) {
        ...
        if (mGraph != null && mBackStack.isEmpty()) {
            boolean deepLinked = !mDeepLinkHandled && mActivity != null
                    && handleDeepLink(mActivity.getIntent());
            if (!deepLinked) {
                // Navigate to the first destination in the graph
                // if we haven't deep linked to a destination
                navigate(mGraph, startDestinationArgs, null, null);
            }
        } else {
            dispatchOnDestinationChanged();
        }
    }

剛開始的時(shí)候會(huì)執(zhí)行到navigate方法。

9. NavController#navigate

    private void navigate(@NonNull NavDestination node, @Nullable Bundle args,
            @Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {
        boolean popped = false;
        boolean launchSingleTop = false;
        if (navOptions != null) {
            if (navOptions.getPopUpTo() != -1) {
                popped = popBackStackInternal(navOptions.getPopUpTo(),
                        navOptions.isPopUpToInclusive());
            }
        }
        Navigator<NavDestination> navigator = mNavigatorProvider.getNavigator(
                node.getNavigatorName());
        Bundle finalArgs = node.addInDefaultArgs(args);
        NavDestination newDest = navigator.navigate(node, finalArgs,
                navOptions, navigatorExtras);
        ...
    }

根據(jù)分析得出getNavigator獲取到的Navigator是NavGraphNavigator實(shí)例。

10. NavGraphNavigator#navigate

    @Nullable
    @Override
    public NavDestination navigate(@NonNull NavGraph destination, @Nullable Bundle args,
            @Nullable NavOptions navOptions, @Nullable Extras navigatorExtras) {
        int startId = destination.getStartDestination();
        if (startId == 0) {
            throw new IllegalStateException("no start destination defined via"
                    + " app:startDestination for "
                    + destination.getDisplayName());
        }
        NavDestination startDestination = destination.findNode(startId, false);
        if (startDestination == null) {
            final String dest = destination.getStartDestDisplayName();
            throw new IllegalArgumentException("navigation destination " + dest
                    + " is not a direct child of this NavGraph");
        }
        Navigator<NavDestination> navigator = mNavigatorProvider.getNavigator(
                startDestination.getNavigatorName());
        return navigator.navigate(startDestination, startDestination.addInDefaultArgs(args),
                navOptions, navigatorExtras);
    }

navigate方法中通過startId找到NavDestination變量,再根據(jù)NavDestination#getNavigatorName方法獲取到的名稱得到對(duì)應(yīng)的Navigator實(shí)例,此處獲取到的是FragmentNavigator實(shí)例。

11. FragmentNavigator#navigate

    @Nullable
    @Override
    public NavDestination navigate(@NonNull Destination destination, @Nullable Bundle args,
            @Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {
        ...
        String className = destination.getClassName();
        if (className.charAt(0) == '.') {
            className = mContext.getPackageName() + className;
        }
        final Fragment frag = instantiateFragment(mContext, mFragmentManager,
                className, args);
        frag.setArguments(args);
        final FragmentTransaction ft = mFragmentManager.beginTransaction();

        int enterAnim = navOptions != null ? navOptions.getEnterAnim() : -1;
        int exitAnim = navOptions != null ? navOptions.getExitAnim() : -1;
        int popEnterAnim = navOptions != null ? navOptions.getPopEnterAnim() : -1;
        int popExitAnim = navOptions != null ? navOptions.getPopExitAnim() : -1;
        if (enterAnim != -1 || exitAnim != -1 || popEnterAnim != -1 || popExitAnim != -1) {
            enterAnim = enterAnim != -1 ? enterAnim : 0;
            exitAnim = exitAnim != -1 ? exitAnim : 0;
            popEnterAnim = popEnterAnim != -1 ? popEnterAnim : 0;
            popExitAnim = popExitAnim != -1 ? popExitAnim : 0;
            ft.setCustomAnimations(enterAnim, exitAnim, popEnterAnim, popExitAnim);
        }

        ft.replace(mContainerId, frag);
        ft.setPrimaryNavigationFragment(frag);

        final @IdRes int destId = destination.getId();
        final boolean initialNavigation = mBackStack.isEmpty();
        // TODO Build first class singleTop behavior for fragments
        final boolean isSingleTopReplacement = navOptions != null && !initialNavigation
                && navOptions.shouldLaunchSingleTop()
                && mBackStack.peekLast() == destId;

        boolean isAdded;
        if (initialNavigation) {
            isAdded = true;
        } else if (isSingleTopReplacement) {
            // Single Top means we only want one instance on the back stack
            if (mBackStack.size() > 1) {
                // If the Fragment to be replaced is on the FragmentManager's
                // back stack, a simple replace() isn't enough so we
                // remove it from the back stack and put our replacement
                // on the back stack in its place
                mFragmentManager.popBackStack(
                        generateBackStackName(mBackStack.size(), mBackStack.peekLast()),
                        FragmentManager.POP_BACK_STACK_INCLUSIVE);
                ft.addToBackStack(generateBackStackName(mBackStack.size(), destId));
            }
            isAdded = false;
        } else {
            ft.addToBackStack(generateBackStackName(mBackStack.size() + 1, destId));
            isAdded = true;
        }
        if (navigatorExtras instanceof Extras) {
            Extras extras = (Extras) navigatorExtras;
            for (Map.Entry<View, String> sharedElement : extras.getSharedElements().entrySet()) {
                ft.addSharedElement(sharedElement.getKey(), sharedElement.getValue());
            }
        }
        ft.setReorderingAllowed(true);
        ft.commit();
        // The commit succeeded, update our view of the world
        if (isAdded) {
            mBackStack.add(destId);
            return destination;
        } else {
            return null;
        }
    }

navigate方法中使用的是FragmentFactory(反射)創(chuàng)建fragment實(shí)例。最后通過FragmentTransaction#replace方法添加fragment實(shí)例。

再回頭看NavHostFragment的生命周期onCreateView方法。

11. NavHostFragment#onCreateView

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {
        FragmentContainerView containerView = new FragmentContainerView(inflater.getContext());
        // When added via XML, this has no effect (since this FragmentContainerView is given the ID
        // automatically), but this ensures that the View exists as part of this Fragment's View
        // hierarchy in cases where the NavHostFragment is added programmatically as is required
        // for child fragment transactions
        containerView.setId(getContainerId());
        return containerView;
    }

NavHostFragment默認(rèn)展示視圖是FragmentContainerView。

12. NavHostFragment#onViewCreated

    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        Navigation.setViewNavController(view, mNavController);
        // When added programmatically, we need to set the NavController on the parent - i.e.,
        // the View that has the ID matching this NavHostFragment.
        if (view.getParent() != null) {
            mViewParent = (View) view.getParent();
            if (mViewParent.getId() == getId()) {
                Navigation.setViewNavController(mViewParent, mNavController);
            }
        }
    }
    public static void setViewNavController(@NonNull View view,
            @Nullable NavController controller) {
        view.setTag(R.id.nav_controller_view_tag, controller);
    }
    private static NavController findViewNavController(@NonNull View view) {
        while (view != null) {
            NavController controller = getViewNavController(view);
            if (controller != null) {
                return controller;
            }
            ViewParent parent = view.getParent();
            view = parent instanceof View ? (View) parent : null;
        }
        return null;
    }
    private static NavController getViewNavController(@NonNull View view) {
        Object tag = view.getTag(R.id.nav_controller_view_tag);
        NavController controller = null;
        if (tag instanceof WeakReference) {
            controller = ((WeakReference<NavController>) tag).get();
        } else if (tag instanceof NavController) {
            controller = (NavController) tag;
        }
        return controller;
    }

將NavController設(shè)置為NavHostFragment的根視圖View的tag,以后調(diào)用Navigation#findNavController時(shí), 會(huì)從傳入視圖及其所有父視圖中找tag,直到找到NavController為止。

從以上分析可以看出,每次調(diào)用NavController#navigate方法都會(huì)重新生成一個(gè)新的Fragment并且調(diào)用FragmentTransaction#replace添加,所以每次都會(huì)看到重建Fragment的現(xiàn)象。

解決方案

自定義Navigator重寫Navigator#navigate方法。

@Navigator.Name("customNavigator")
public class CustomNavigator extends FragmentNavigator {

    private Context context;
    private FragmentManager fragmentManager;
    private int containerId;

    public CustomNavigator(@NonNull Context context, @NonNull FragmentManager fragmentManager, int containerId) {
        super(context, fragmentManager, containerId);

        this.context = context;
        this.fragmentManager = fragmentManager;
        this.containerId = containerId;
    }
    @Nullable
    @Override
    public NavDestination navigate(@NonNull Destination destination, @Nullable Bundle args, @Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {
        FragmentTransaction ft = fragmentManager.beginTransaction();
        // 獲取當(dāng)前顯示的Fragment
        Fragment fragment = fragmentManager.getPrimaryNavigationFragment();
        if (fragment != null) {
            ft.hide(fragment);
        }
        final String tag = String.valueOf(destination.getId());
        fragment = fragmentManager.findFragmentByTag(tag);
        if (fragment != null) {
            ft.show(fragment);
        } else {
            fragment = instantiateFragment(context, fragmentManager, destination.getClassName(), args);
            ft.add(containerId, fragment, tag);
        }
        ft.setPrimaryNavigationFragment(fragment);
        ft.setReorderingAllowed(true);
        ft.commit();
        return destination;
    }
}

然后通過NavigatorProvider添加即可:

NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
navController.getNavigatorProvider().addNavigator(new CustomNavigator(this, getSupportFragmentManager(), R.id.nav_host_fragment));

到此這篇關(guān)于Android Navigation重建Fragment問題分析及解決的文章就介紹到這了,更多相關(guān)Android Navigation 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Android程序啟動(dòng)時(shí)出現(xiàn)黑屏問題的解決方法

    Android程序啟動(dòng)時(shí)出現(xiàn)黑屏問題的解決方法

    這篇文章主要介紹了Android程序啟動(dòng)時(shí)出現(xiàn)黑屏問題的解決方法,分析了黑屏出現(xiàn)的原因及相應(yīng)的解決方法,需要的朋友可以參考下
    2016-08-08
  • android下拉刷新ListView的介紹和實(shí)現(xiàn)代碼

    android下拉刷新ListView的介紹和實(shí)現(xiàn)代碼

    在當(dāng)下,列表組件不帶下拉刷新的都不好意思叫列表。第一次完成列表的下拉刷新功能的時(shí)候,直接在Activity中實(shí)現(xiàn),雖然功能上是實(shí)現(xiàn)了,總體上感覺很亂。所以第二次用到的時(shí)候,就想著封裝成一個(gè)組件,實(shí)現(xiàn)和Activity的解耦。
    2013-04-04
  • React-Native  Android 與 IOS App使用一份代碼實(shí)現(xiàn)方法

    React-Native Android 與 IOS App使用一份代碼實(shí)現(xiàn)方法

    這篇文章主要介紹了React-Native Android 與 IOS App使用一份代碼實(shí)現(xiàn)方法的相關(guān)資料,這里舉例說明,該如何實(shí)現(xiàn)IOS和Android APP 都使用一樣的代碼,需要的朋友可以參考下
    2016-12-12
  • Android開發(fā)之ToggleButton實(shí)現(xiàn)開關(guān)效果示例

    Android開發(fā)之ToggleButton實(shí)現(xiàn)開關(guān)效果示例

    這篇文章主要介紹了Android開發(fā)之ToggleButton實(shí)現(xiàn)開關(guān)效果的方法,結(jié)合實(shí)例形式分析了ToggleButton控件實(shí)現(xiàn)開關(guān)效果的布局與功能相關(guān)操作技巧,需要的朋友可以參考下
    2017-07-07
  • 從零開始學(xué)android小示例程序

    從零開始學(xué)android小示例程序

    這篇文章主要介紹了一個(gè)學(xué)習(xí)android開發(fā)的小示例程序,需要的朋友可以參考下
    2014-02-02
  • Flutter實(shí)現(xiàn)底部彈窗效果

    Flutter實(shí)現(xiàn)底部彈窗效果

    本文詳細(xì)講解了Flutter實(shí)現(xiàn)底部彈窗效果的方法,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-11-11
  • Android使用友盟集成QQ、微信、微博等第三方分享與登錄方法詳解

    Android使用友盟集成QQ、微信、微博等第三方分享與登錄方法詳解

    之前的項(xiàng)目第三方分享和登錄一直都使用ShareSDK實(shí)現(xiàn)的。為了統(tǒng)一使用友盟的全家桶,所以三方分享和登錄也就選擇了友盟,這里為大家整理出詳細(xì)方法
    2018-03-03
  • android布局屬性詳解分享

    android布局屬性詳解分享

    這篇文章主要介紹了android各種布局中用到的屬性中文說明,需要的朋友可以參考下
    2014-02-02
  • Android ListView構(gòu)建支持單選和多選的投票項(xiàng)目

    Android ListView構(gòu)建支持單選和多選的投票項(xiàng)目

    如何在Android的ListView中構(gòu)建CheckBox和RadioButton列表?這篇文章主要為大家詳細(xì)介紹了Android ListView實(shí)現(xiàn)支持單選和多選的投票項(xiàng)目,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-01-01
  • Android學(xué)習(xí)教程之2D繪圖基礎(chǔ)及繪制太極圖

    Android學(xué)習(xí)教程之2D繪圖基礎(chǔ)及繪制太極圖

    這篇文章主要給大家介紹了Android中2D繪圖基礎(chǔ)的相關(guān)資料,文中介紹了繪圖的基礎(chǔ)內(nèi)容,以及通過Canvas和Paint實(shí)現(xiàn)繪制太極圖的詳細(xì)過程,對(duì)各位Android新手開發(fā)者們具有一定的參考價(jià)值,需要的朋友下面來一起看看吧。
    2017-04-04

最新評(píng)論