一起来搭简单的App框架

本文介绍了一款常用App框架的设计与实现过程,重点讲解了使用RadioGroup优化选项卡布局、ToolBar与侧滑菜单的联动效果、ViewPager结合Fragment实现内容切换等功能。

1、概述

最近在学车,抽着空写了一个常用的App框架,可惜不会制作gif图片,请忽略录屏软件的那个浮动窗口,要是有更好的方式制作gif麻烦告知下提前感谢,效果图如下,凑合着看下吧。


主要实现了

【1】使用RadioGroup处理选项卡优化布局
【2】使用ToolBar跟侧滑图表联动,效果更好
【3】集合了viewerPager+fragment,drawerlayout+toolbar,optionmenu以及circleImageView处理头像等功能

一般app项目应该可以应付了。下面说下实现


2、选项卡按钮

这里使用了RadioGroup,以前用的是多层linearlayout结合weight属性,再使用ImageView和TextView感觉有点过度绘制,这里做了优化采用RadioGroup

<span style="font-size:18px;"><?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    android:id="@+id/bottom"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <RadioGroup

        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:background="@color/mainpage_mycrircle_leftview_item_auth_name"
        android:orientation="horizontal">

        <RadioButton
            android:id="@+id/id_tab_mycircle"
            style="@style/style_RadioButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:checked="true"
            android:drawableTop="@drawable/mainpage_tab_mycircle"
            android:text="我的圈子"
            />

        <RadioButton
            android:id="@+id/id_tab_discovery"
            style="@style/style_RadioButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:checked="true"
            android:drawableTop="@drawable/mainpage_tab_discovery"
            android:text="发现"/>

        <RadioButton
            android:id="@+id/id_tab_message"
            style="@style/style_RadioButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:checked="true"
            android:drawableTop="@drawable/mainpage_tab_message"
            android:text="消息"/>

        <RadioButton
            android:id="@+id/id_tab_setting"
            style="@style/style_RadioButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:checked="true"
            android:drawableTop="@drawable/mainpage_tab_setting"
            android:text="个人中心"
            />

    </RadioGroup>


</RelativeLayout>
</span>

出来的效果是这样的感觉还可以,布局也很简单

不过要注意对radioButton的style做一番更改换掉原来的属性 styles.xml中

<span style="font-size:18px;"><style name="style_RadioButton">
        <item name="android:button">@null</item>
        <item name="android:background">@null</item>
        <item name="android:layout_weight">0.25</item>
        <item name="android:gravity">center</item>
        <item name="android:textColor">@color/gray</item>
        <item name="android:textSize">12sp</item>
    </style></span>



3、ViewPager+fragment实现选项卡内容区域

主要是viewPager使用的FragmentPagerAdapter
如何使用ViewPager和他的三种适配器请参考我的上一篇博客http://blog.csdn.net/xsf50717/article/details/49764521 这里就不在详细讲了,看一下核心代码

首先我们需要4个fragment对应4个选项卡,然后在viewPager中管理这4个,核心代码在MainActivity.java中

<span style="font-size:18px;">private void initViewPages() {
        //初始化四个布局
        Fragment01 tab01 = new Fragment01();
        Fragment02 tab02 = new Fragment02();
        Fragment03 tab03 = new Fragment03();
        Fragment04 tab04 = new Fragment04();
        mFragments.add(tab01);
        mFragments.add(tab02);
        mFragments.add(tab03);
        mFragments.add(tab04);
        //初始化Adapter这里使用FragmentPagerAdapter
        mAdpter = new FragmentPagerAdapter(getSupportFragmentManager()) {
            @Override
            public Fragment getItem(int position) {

                return mFragments.get(position);
            }

            @Override
            public int getCount() {
                return mFragments.size();
            }


        };
        mViewPager.setAdapter(mAdpter);

    }</span>

然后逻辑处理在MainActivity的initEvent()中,对RadioGroup和Viewpager滑动监听

<span style="font-size:18px;"> //监听RadioGroup
        mGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup group, int checkedId) {
                switch (checkedId) {
                    case R.id.id_tab_mycircle:
                        mViewPager.setCurrentItem(0, false);
                        break;
                    case R.id.id_tab_discovery:
                        mViewPager.setCurrentItem(1, false);
                        break;
                    case R.id.id_tab_message:
                        mViewPager.setCurrentItem(2, false);
                        break;
                    case R.id.id_tab_setting:
                        mViewPager.setCurrentItem(3, false);
                        break;
                    default:
                        break;
                }
            }
        });
        //监听Page滑动
        mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

            }

            @Override
            public void onPageSelected(int position) {
                //当viewPager滑动的时候
                switch (position) {
                    case 0:
                        mGroup.check(R.id.id_tab_mycircle);
                        break;
                    case 1:
                        mGroup.check(R.id.id_tab_discovery);
                        break;
                    case 2:
                        mGroup.check(R.id.id_tab_message);
                        break;
                    case 3:
                        mGroup.check(R.id.id_tab_setting);
                        break;
                    default:
                        break;
                }

            }

            @Override
            public void onPageScrollStateChanged(int state) {

            }
        });
</span>

4、侧滑联动

这里使用了谷歌官方的的DrawerLayout(support V4包)+ToolBar(support V7包)方式,需要在android stuido中实现导入这俩个库,一般V7库默认就存在了

4.1、DrawerLayout的使用

DrawerLayout使用比较简单,必须把DrawerLayout作为布局的跟标签。然后在跟标签中添加一个包含内容的视图,就是当抽屉完全隐藏的时候显示的内容布局,然后 添加的就是抽屉布局,这个布局可以按照需求自己定义,顺序不要搞反了

本例的抽屉布局drawerlayout.xml代码如下

<span style="font-size:18px;"><?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
    android:id="@+id/id_drawerlayout"
    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">

    <RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/white">


        <RadioGroup
            android:id="@+id/radioGroup"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:background="@color/mainpage_mycrircle_leftview_item_auth_name"
            android:orientation="horizontal">

            <RadioButton
                android:id="@+id/id_tab_mycircle"
                style="@style/style_RadioButton"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:checked="true"
                android:drawableTop="@drawable/mainpage_tab_mycircle"
                android:text="我的圈子"
                />

            <RadioButton
                android:id="@+id/id_tab_discovery"
                style="@style/style_RadioButton"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"

                android:drawableTop="@drawable/mainpage_tab_discovery"
                android:text="发现"/>

            <RadioButton
                android:id="@+id/id_tab_message"
                style="@style/style_RadioButton"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"

                android:drawableTop="@drawable/mainpage_tab_message"
                android:text="消息"/>

            <RadioButton
                android:id="@+id/id_tab_setting"
                style="@style/style_RadioButton"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"

                android:drawableTop="@drawable/mainpage_tab_setting"
                android:text="个人中心"
                />

        </RadioGroup>

        <android.support.v4.view.ViewPager
            android:id="@+id/id_viewpager"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_above="@id/radioGroup"
            >
        </android.support.v4.view.ViewPager>


    </RelativeLayout>

    <LinearLayout
        android:id="@+id/id_drawer"
        android:layout_width="240dp"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:background="#E0EEE0"
        android:orientation="vertical">

        <com.elvis.utils.CircleImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_marginTop="10dp"
            android:src="@drawable/xsfelvis"
            app:border_color="@color/page_item_black_50"
            app:border_width="2dp"
            />

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:orientation="horizontal">

            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@drawable/banner_left"
                />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:paddingTop="5dp"
                android:text="xsfelvis"
                android:textSize="15sp"


                />

            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@drawable/banner_right"
                />

        </LinearLayout>

        <ListView
            android:id="@+id/id_lv"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:cacheColorHint="#00000000"
            android:footerDividersEnabled="false"
            android:gravity="center"
            android:listSelector="@android:color/transparent">
        </ListView>
    </LinearLayout>
</android.support.v4.widget.DrawerLayout>
</span>


我们可以看出,内容布局在RelativeLayout中,抽屉布局在LinearLayout中,内容布局包含radiogroup,viewpager俩部分,而抽屉布局包含一个CircleImageView和一个listview


4.2 ToolBar的使用

由于Toolbar是继承自View,所以可以像其他标准控件一样直接主布局文件添加Toolbar,但是为了提高Toolbar的重用效率,可以在layout创建一个custom_toolbar.xml代码如下:

<span style="font-size:18px;"><?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar
    android:id="@+id/tl_custom"

    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="wrap_content"
    android:background="?attr/colorPrimaryDark"
    android:fitsSystemWindows="true"
    android:minHeight="?attr/actionBarSize"
    android:popupTheme="@style/ThemeOverlay.AppCompat.Light"
    app:theme="@style/ThemeOverlay.AppCompat.ActionBar">
</android.support.v7.widget.Toolbar>
</span>
使用Toolbar一定要将styles中apptheme重新设置styles.xml

<span style="font-size:18px;">   <style name="AppBaseTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- 状态栏和标题栏颜色-->
        <!--<item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>-->
        <item name="colorPrimaryDark">@color/Indigo_colorPrimaryDark</item>
        <!--Toolbar颜色-->
        <item name="colorPrimary">@color/Indigo_colorPrimary</item>

        <!-- 标题颜色-->
        <item name="android:textColorPrimary">@android:color/white</item>
        <!-- 溢出菜单图标颜色-->
        <item name="colorControlNormal">@android:color/white</item>

        <!-- 箭头 -->
        <item name="drawerArrowStyle">@style/DrawerArrowStyle</item>
        <!-- 溢出菜单文字颜色-->
        <item name="textAppearanceLargePopupMenu">@style/OverflowMenuTextAppearance</item>
        <!-- 菜单项点击selector-->
        <item name="actionBarItemBackground">@drawable/abc_item_background_holo_dark</item>

    </style>
    <!-- 左边的箭头指示-->
    <style name="DrawerArrowStyle" parent="Widget.AppCompat.DrawerArrowToggle">
        <item name="spinBars">true</item>
        <item name="color">@android:color/white</item>
    </style>

    <!--option菜单文字样式-->
    <style name="OverflowMenuTextAppearance" parent="@style/TextAppearance.AppCompat.Widget.PopupMenu.Large">
        <item name="android:textColor">@color/overflowTextColor</item>
    </style>
</span>




4.3、DrawerLayout与Toolbar联动

从效果图也可以看出,我们在滑动中从横线变为箭头,效果有点炫酷,在MainActivity.java中initEvent()中

<span style="font-size:18px;">  //ToolBar

        toolbar.setTitle("APP FrameWork");//设置Toolbar标题
        toolbar.setTitleTextColor(Color.parseColor("#ffffff")); //设置标题颜色

        setSupportActionBar(toolbar);
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        getSupportActionBar().setHomeButtonEnabled(true);

        mDrawerToggle = new ActionBarDrawerToggle(this,
                mDrawerLayout,
                toolbar,
                R.string.drawopen,
                R.string.drawclose) {
            @Override
            public void onDrawerOpened(View drawerView) {
                super.onDrawerOpened(drawerView);
                toolbar.setTitle("侧滑栏");

            }

            @Override
            public void onDrawerClosed(View drawerView) {
                super.onDrawerClosed(drawerView);
                toolbar.setTitle("APP FrameWork");
            }


        };
        mDrawerToggle.syncState();
        mDrawerLayout.setDrawerListener(mDrawerToggle);


    }
</span>

5、optionMeun设置

菜单显示出图标,我们需要重新一个方法,使用反射让其显示出来


<span style="font-size:18px;">    @Override
    public boolean onMenuOpened(int featureId, Menu menu) {
        if (menu != null) {
            if (menu.getClass().getSimpleName().equals("MenuBuilder")) {

                try {
                    Method m = menu.getClass().getDeclaredMethod(
                            "setOptionalIconsVisible", Boolean.TYPE);
                    m.setAccessible(true);
                    m.invoke(menu, true);
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }

            }
        }

        return false;//关闭系统menu按键
    }
</span>

最后记得return false 否则你按系统的menu也会从下方出来menu菜单,感觉不是很好看。


到此结束,源码在github上,有用的话就点个赞吧~https://github.com/xsfelvis/AppFrameWork







<think>嗯,用户想用Vue来开发一个族谱管理的App,我得先理清楚需求。首先,用户可能需要管理家族成员的信息,包括姓名、性别、出生日期这些基本信息,还有成员之间的关系,比如父子、夫妻等。然后,可能需要一个可视化的家谱树来展示整个结构,这样看起来更直观。 接下来,技术选型方面,Vue.js作为前端框架是确定的,但可能需要配一些库。比如,数据可视化部分,D3.js或者GoJS都是不错的选择,不过D3的学习曲线可能比较陡,GoJS有现成的树形图组件,可能更方便。或者用现成的Vue组件,比如vue-org-tree,这样开发起来更快。 状态管理方面,简单的应用可能用Vuex就足够了,但如果数据复杂的话,可能需要更细致的设计。数据存储的话,考虑到可能需要持久化,可以先用本地存储比如localStorage,或者IndexedDB,后续再考虑接入后端服务,比如Firebase或者REST API。 然后是功能模块,用户需要添加、编辑、删除成员,以及建立关系。这里的关系建立可能需要双向的,比如设置父亲的同时自动添加孩子。可视化展示方面,可能需要拖拽节点、展开折叠分支,以及点击查看详细信息。 数据结构的定义也很重要。每个成员应该有唯一ID,姓名、性别、出生日期等基本信息,还有父母、配偶、孩子的引用。可能需要一个数组存储所有成员,然后用ID来关联关系。比如: ```javascript members: [ { id: 1, name: '张三', gender: 'male', parents: [], spouse: 2, children: [3] }, { id: 2, name: '李四', gender: 'female', parents: [], spouse: 1, children: [3] }, { id: 3, name: '张小小', gender: 'male', parents: [1,2], spouse: null, children: [] } ] ``` 这样设计的话,查找和更新关系可能会比较方便,但需要注意数据一致性问题,比如删除一个成员时,需要更新其他相关成员的关系。 接下来是组件结构。可能需要一个主组件FamilyTree,包含成员列表MemberList和可视化树FamilyTreeView。MemberList负责显示所有成员,提供添加、编辑、删除的操作。FamilyTreeView用图形展示树状结构,可能需要使用第三方库来渲染。 在实现添加成员时,要考虑如何关联现有成员。比如,添加孩子的时候,可能需要选择父亲和母亲,然后自动更新双方的孩子列表。编辑成员时,可能需要修改其关系,这时候要处理可能存在的循环引用或者错误关系。 可视化部分,如果使用vue-org-tree,可能需要将数据结构转换成组件需要的格式,比如嵌套的children结构。可能需要一个递归函数将扁平的数据结构转换为树形结构,找到根节点(没有父母的成员),然后递归地构建每个节点的子节点。 另外,数据持久化方面,每次操作后需要将数据保存到localStorage,这样页面刷新后数据不会丢失。可以监听Vuex的mutations,自动保存数据,或者在每次修改后手动触发保存。 测试的时候,可能会遇到数据不一致的问题,比如某个成员的父亲指向了一个不存在的ID,这时候需要添加校验逻辑,或者在渲染时处理异常情况,避免应用崩溃。 用户可能还希望导出或导入数据,比如生成JSON文件,或者打印家谱图。这部分功能可以作为后续扩展,但需要提前考虑数据格式的兼容性。 响应式设计也是需要考虑的,确保在手机和电脑上都能良好显示。可视化部分可能需要适配不同屏幕尺寸,使用媒体查询或者动态调整布局。 最后,可能还需要考虑权限管理和多用户支持,但如果是单机应用的话,可以先不做这部分,专注于核心功能。 总结下来,开发步骤大致是:建Vue项目,设计数据结构,实现成员管理功能,集成可视化库,处理数据持久化,测试优化,然后逐步扩展高级功能。用户可能需要一个简单易用的界面,所以UI设计也要尽量直观,操作流程顺畅。</think>以下是一个使用 Vue.js 开发族谱管理应用的实现思路和技术方案,分为核心功能、技术选型和代码示例三部分: --- ### 一、核心功能设计 1. **成员管理** - 添加/编辑成员信息(姓名、性别、出生日期、照片等) - 建立成员关系(父子、配偶、兄弟姐妹) - 删除成员(需处理关联关系) 2. **可视化家谱树** - 树形结构展示家族关系 - 支持缩放/拖动查看大家族 - 点击节点查看详细信息 3. **数据管理** - 本地存储(或对接后端API) - 导入/导出家族数据(JSON格式) - 数据校验与恢复 --- ### 二、技术选型 | 类别 | 推荐方案 | |--------------|-----------------------------------| | 前端框架 | Vue 3 + Composition API | | 可视化库 | D3.js 或 GoJS(专业关系图) | | UI组件库 | Element Plus | | 状态管理 | Pinia | | 数据存储 | IndexedDB 或 Firebase | | 关系算法 | 自定义树形结构处理 | -- ### 三、代码示例 #### 1. 成员数据结构 ```javascript // stores/familyStore.js export const useFamilyStore = defineStore('family', { state: () => ({ members: [ { id: 1, name: '王大明', gender: 'male', birth: '1950-01-01', parents: [], spouses: [2], children: [3] } ] }), actions: { addMember(member) { this.members.push({ id: Date.now(), ...member, spouses: [], children: [] }) } } }) ``` #### 2. 家谱树组件 ```vue <!-- components/FamilyTree.vue --> <template> <div class="tree-container" ref="container"> <div v-for="node in treeData" :key="node.id" class="node"> <div class="member-card"> <h3>{{ node.name }}</h3> <p>{{ node.birth }}</p> </div> <FamilyTree v-if="node.children" :nodes="node.children"/> </div> </div> </template> <script setup> import { computed } from 'vue' const props = defineProps(['nodes']) // 将扁平数据转换为树形结构 const treeData = computed(() => { return buildTree(props.nodes) }) function buildTree(members) { // 实现树形结构转换逻辑 // 返回包含嵌套children的结构 } </script> ``` #### 3. 关系建立方法示例 ```javascript // utils/relationship.js export function setParentChild(parentId, childId) { const familyStore = useFamilyStore() familyStore.members.forEach(m => { if(m.id === parentId && !m.children.includes(childId)) { m.children.push(childId) } if(m.id === childId && !m.parents.includes(parentId)) { m.parents.push(parentId) } }) } ``` --- ### 四、进阶功能建议 1. **时间轴功能**:展示家族历史事件 2. **血缘计算**:自动计算亲属关系称谓 3. **隐私保护**:敏感信息加密处理 4. **分享功能**:生成可分享的家族图谱链接 5. **智能推荐**:通过姓名分析地域来源 --- ### 五、开发建议 1. 优先实现核心关系管理逻辑 2. 使用拖拽库(如 Vue.Draggable)实现可视化关系编辑 3. 添加撤销/重做功能保障数据安全 4. 采用响应式设计适配移动端 5. 使用Cytoscape.js实现专业图谱可视化 需要更详细的实现某个具体功能(如血缘计算算法),可以告诉我具体需求!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值