news 2026/4/23 14:56:23

基于Java+MySQL实现 Android APP 花艺分享平台

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于Java+MySQL实现 Android APP 花艺分享平台

花艺分享平台

一、课程设计的目的与要求

通过软件开发的实践训练,使学生进一步掌握软件工程的方法和技术,提高软件开发的实际能力,培养工程设计能力和综合分析、解决问题的能力。

具体如下:

学习和实践在分析和设计计算机应用系统所需要的知识,包括面向对象的系统分析与设计,编码和测试方面的知识;熟悉自动化的软件开发工具 AndroidStudio3,并将其运用于软件开发的全过程;进一步加强和提高软件工程文档的编写能力;

二、设计正文

1.概述

1.1 课题描述

随着网络信息的发展,网络在人们生活中的应用越来越广泛。人们分享自己的生活。但生活的任务繁重,人们在繁忙的工作生活中分享花的生活带来了很大的麻烦,该“花艺”应用 App,提供给喜爱花植物的用户一个分享平台,用户可以分享自己的插花作品,分享自己精美的图片内容。

1.2 系统目标

“花艺”应用 App,提供给喜爱花植物的用户一个分享平台,用户可以分享自己的插花作品,分享自己精美的图片内容。同时该平台还是提供给用户分享好看的花签小卡片,卡片里记录可以记录日常或者记录记录学习内容以及花艺作品心得,打造一个花类植物的知识信息以及花艺共享平台。同时使用了隐式意图调用手机相机和相册,以及基本逻辑功能实现。

1.3 环境

a) 操作系统: Windows10

b) 使用软件:

代码编写:AndroidStudio3

数据库:MySQL

文档编写:Microsoft Word 2007.

c) 开发语言:Java

2.系统需求分析

花艺 App 是一款手机应用软件,提供服务主要为:花艺学堂,每日推出原创插花,为花艺爱好者提供美学指导;原创精美壁纸,每日更新,自动匹配适应手机屏幕;花艺研究社,为爱花人事和从业者提供系统的花艺教程。较为灵活的文章管理功能。

根据应用的具体情况,系统的主要功能包括:

用户功能 :

功能:登录、退出、个人管理

花签日历卡:浏览花钱内容和管理

个人花签管理:查看花签、修改花签、删除花签,增加花签

知乎新闻:热点专题和其他专题

管理员功能:

登录、退出、个人管理

花签管理:查看花签、修改花签、删除花签,增加花签

知乎新闻:热点专题和其他专题

2.1 用例图

管理者用例:

1.登录:管理员登录。

图表 a.1 管理者登录用例图

图表 1 管理员管理花签

客户用例图

1.登录:用户登录。

图表 b.1 客户登录用例图

图表 2 用户管理花签

2.2 项目结构

3.系统总体设计

系统模块图:

4.详细设计

5.系统实现

具体代码已打包

6.测试

登录界面

三、参考文献

李宁宁.《基于 Android Studio 的应用程序开发教程》

郭霖. 《第一行代码》

部分源代码

package com.example.liuyi; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.Menu; import android.widget.ImageView; import android.widget.Toast; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.TypeReference; import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.google.android.material.snackbar.Snackbar; import com.google.android.material.navigation.NavigationView; import androidx.navigation.NavController; import androidx.navigation.Navigation; import androidx.navigation.ui.AppBarConfiguration; import androidx.navigation.ui.NavigationUI; import androidx.drawerlayout.widget.DrawerLayout; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; import org.xutils.common.Callback; import org.xutils.http.RequestParams; import org.xutils.x; import java.util.HashMap; import java.util.List; public class MainActivity extends AppCompatActivity { private AppBarConfiguration mAppBarConfiguration; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); DrawerLayout drawer = findViewById(R.id.drawer_layout); NavigationView navigationView = findViewById(R.id.nav_view); // Passing each menu ID as a set of Ids because each // menu should be considered as top level destinations. mAppBarConfiguration = new AppBarConfiguration.Builder( R.id.nav_gallery, R.id.nav_home, R.id.nav_slideshow,R.id.nav_share) .setDrawerLayout(drawer) .build(); NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment); NavigationUI.setupActionBarWithNavController(this, navController, mAppBarConfiguration); NavigationUI.setupWithNavController(navigationView, navController); } // private void getPirture(final String result, View view) { // final ImageView imageView = view.findViewById(R.id.imageView); // // imageView.setOnClickListener(new View.OnClickListener() { // @Override // public void onClick(View v) { // Glide.with(MainActivity.this).load(result) // .fitCenter() // .centerCrop()//会裁减图片填充布局 // .diskCacheStrategy(DiskCacheStrategy.ALL) // .placeholder(R.drawable.hua_daisy).into(imageView); // } // }); // // // } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public boolean onSupportNavigateUp() { NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment); return NavigationUI.navigateUp(navController, mAppBarConfiguration) || super.onSupportNavigateUp(); } }
package com.example.liuyi; import android.content.Context; import android.util.AttributeSet; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; import androidx.annotation.Nullable; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.StaggeredGridLayoutManager; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import com.example.liuyi.ui.footerView.BaseFooterView; import com.example.liuyi.ui.footerView.SimpleFooterView; /** * @auther deadline * @time 2016/10/22 * SwipeRefreshLayout + recyclerView */ public class SwipeRecyclerView extends FrameLayout implements SwipeRefreshLayout.OnRefreshListener{ private View mEmptyView; private BaseFooterView mFootView; private RecyclerView recyclerView; private SwipeRefreshLayout mRefreshLayout; private RecyclerView.LayoutManager mLayoutManager; private OnLoadListener mListener; private GridLayoutManager.SpanSizeLookup mSpanSizeLookup; private DataObserver mDataObserver; private WrapperAdapter mWrapperAdapter; private boolean isEmptyViewShowing; private boolean isLoadingMore; private boolean isLoadMoreEnable; private boolean isRefreshEnable; private int lastVisiablePosition = 0; public SwipeRecyclerView(Context context) { this(context, null); } public SwipeRecyclerView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public SwipeRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); setupSwipeRecyclerView(); } private void setupSwipeRecyclerView() { isEmptyViewShowing = false; isRefreshEnable = true; isLoadingMore = false; isLoadMoreEnable = true; mFootView = new SimpleFooterView(getContext()); View view = LayoutInflater.from(getContext()).inflate(R.layout.layout_swipe_recyclerview, this); mRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.SwipeRefreshLayout); recyclerView = (RecyclerView) view.findViewById(R.id.RecyclerView); mLayoutManager = recyclerView.getLayoutManager(); mRefreshLayout.setOnRefreshListener(this); recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); } @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); // do nothing if load more is not enable or refreshing or loading more if(!isLoadMoreEnable || isRefreshing() || isLoadingMore){ return; } //get the lastVisiablePosition mLayoutManager = recyclerView.getLayoutManager(); if(mLayoutManager instanceof LinearLayoutManager){ lastVisiablePosition = ((LinearLayoutManager)mLayoutManager).findLastVisibleItemPosition(); }else if(mLayoutManager instanceof GridLayoutManager){ lastVisiablePosition = ((GridLayoutManager)mLayoutManager).findLastCompletelyVisibleItemPosition(); }else if(mLayoutManager instanceof StaggeredGridLayoutManager){ int[] into = new int[((StaggeredGridLayoutManager) mLayoutManager).getSpanCount()]; ((StaggeredGridLayoutManager) mLayoutManager).findLastVisibleItemPositions(into); lastVisiablePosition = findMax(into); } int childCount = mWrapperAdapter == null ? 0 : mWrapperAdapter.getItemCount(); if(childCount > 1 && lastVisiablePosition == childCount - 1){ if(mListener != null){ isLoadingMore = true; mListener.onLoadMore(); } } } }); } private int findMax(int[] lastPositions) { int max = lastPositions[0]; for (int value : lastPositions) { if (value > max) { max = value; } } return max; } /** * set is enable pull to refresh * @param refreshEnable */ public void setRefreshEnable(boolean refreshEnable){ isRefreshEnable = refreshEnable; mRefreshLayout.setEnabled(isRefreshEnable); } public boolean getRefreshEnable(){ return isRefreshEnable; } /** * set is loading more enable * @param loadMoreEnable * if true when recyclerView scroll to bottom load more action will be trigger */ public void setLoadMoreEnable(boolean loadMoreEnable) { if(!loadMoreEnable){ stopLoadingMore(); } isLoadMoreEnable = loadMoreEnable; } public boolean getLoadMoreEnable(){ return isLoadMoreEnable; } /** * get is refreshing * @return */ public boolean isRefreshing(){ return mRefreshLayout.isRefreshing(); } /** * get is loading more * @return */ public boolean isLoadingMore(){ return isLoadingMore; } /** * is empty view showing * @return */ public boolean isEmptyViewShowing(){ return isEmptyViewShowing; } /** * you may need set some other attributes of swipeRefreshLayout * @return * swipeRefreshLayout */ public SwipeRefreshLayout getSwipeRefreshLayout(){ return mRefreshLayout; } /** * you may need set some other attributes of RecyclerView * @return * RecyclerView */ public RecyclerView getRecyclerView(){ return recyclerView; } /** * set load more listener * @param listener */ public void setOnLoadListener(OnLoadListener listener){ mListener = listener; } /** * support for GridLayoutManager * @param spanSizeLookup */ public void setSpanSizeLookup(GridLayoutManager.SpanSizeLookup spanSizeLookup){ this.mSpanSizeLookup = spanSizeLookup; } /** * set the footer view * @param footerView * the view to be showing when pull up */ public void setFooterView(BaseFooterView footerView){ if(footerView != null) { this.mFootView = footerView; } } /** * set a empty view like listview * @param emptyView * the view to be showing when the data set size is zero */ public void setEmptyView(View emptyView){ if(mEmptyView != null){ removeView(mEmptyView); } this.mEmptyView = emptyView; if(mDataObserver != null) { mDataObserver.onChanged(); } } /** * set adapter to recyclerView * @param adapter */ public void setAdapter(RecyclerView.Adapter adapter){ if(adapter != null) { if(mDataObserver == null){ mDataObserver = new DataObserver(); } mWrapperAdapter = new WrapperAdapter(adapter); recyclerView.setAdapter(mWrapperAdapter); adapter.registerAdapterDataObserver(mDataObserver); mDataObserver.onChanged(); } } /** * refresh or load more completed */ public void complete(){ mRefreshLayout.setRefreshing(false); stopLoadingMore(); } /** * set refreshing * if you want load data when first in, you can setRefreshing(true) * after {@link #setOnLoadListener(OnLoadListener)} * @param refreshing */ public void setRefreshing(boolean refreshing){ mRefreshLayout.setRefreshing(refreshing); if(refreshing && !isLoadingMore && mListener != null){ mListener.onRefresh(); } } /** * stop loading more without animation */ public void stopLoadingMore(){ isLoadingMore = false; if(mWrapperAdapter != null) { mWrapperAdapter.notifyItemRemoved(mWrapperAdapter.getItemCount()); } } /** * call method {@link OnLoadListener#onRefresh()} */ @Override public void onRefresh() { if(mListener != null){ //reset footer view status loading if(mFootView != null){ mFootView.onLoadingMore(); } mListener.onRefresh(); } } public void onNetChange(boolean isAvailable) { if(mFootView != null){ mFootView.onNetChange(isAvailable); } } public void onLoadingMore() { if(mFootView != null){ mFootView.onLoadingMore(); } } public void onNoMore(CharSequence message) { if(mFootView != null){ mFootView.onNoMore(message); } } public void onError(CharSequence message) { if(mFootView != null){ mFootView.onError(message); } } private class WrapperAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{ public static final int TYPE_FOOTER = 0x100; RecyclerView.Adapter<RecyclerView.ViewHolder> mInnerAdapter; public WrapperAdapter(RecyclerView.Adapter<RecyclerView.ViewHolder> adapter){ this.mInnerAdapter = adapter; } public boolean isLoadMoreItem(int position){ return isLoadMoreEnable && position == getItemCount() - 1; } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if(TYPE_FOOTER == viewType){ return new FooterViewHolder(mFootView); } return mInnerAdapter.onCreateViewHolder(parent, viewType); } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { if(isLoadMoreItem(position)){ return; } mInnerAdapter.onBindViewHolder(holder, position); } @Override public int getItemViewType(int position) { if(isLoadMoreItem(position)){ return TYPE_FOOTER; }else{ return mInnerAdapter.getItemViewType(position); } } @Override public int getItemCount() { int count = mInnerAdapter == null ? 0 : mInnerAdapter.getItemCount(); //without loadingMore when adapter size is zero if(count == 0){ return 0; } return isLoadMoreEnable ? count + 1 : count; } @Override public long getItemId(int position) { return mInnerAdapter.getItemId(position); } @Override public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) { ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams(); if (lp != null && lp instanceof StaggeredGridLayoutManager.LayoutParams && isLoadMoreItem(holder.getLayoutPosition())) { StaggeredGridLayoutManager.LayoutParams p = (StaggeredGridLayoutManager.LayoutParams) lp; p.setFullSpan(true); } mInnerAdapter.onViewAttachedToWindow(holder); } @Override public void onViewDetachedFromWindow(RecyclerView.ViewHolder holder) { mInnerAdapter.onViewDetachedFromWindow(holder); } @Override public void onAttachedToRecyclerView(RecyclerView recyclerView) { RecyclerView.LayoutManager manager = recyclerView.getLayoutManager(); if (manager instanceof GridLayoutManager) { final GridLayoutManager gridManager = ((GridLayoutManager) manager); gridManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { @Override public int getSpanSize(int position) { boolean isLoadMore = isLoadMoreItem(position); if(mSpanSizeLookup != null && !isLoadMore){ return mSpanSizeLookup.getSpanSize(position); } return isLoadMore ? gridManager.getSpanCount() : 1; } }); } mInnerAdapter.onAttachedToRecyclerView(recyclerView); } @Override public void onDetachedFromRecyclerView(RecyclerView recyclerView) { mInnerAdapter.onDetachedFromRecyclerView(recyclerView); } @Override public boolean onFailedToRecycleView(RecyclerView.ViewHolder holder) { return mInnerAdapter.onFailedToRecycleView(holder); } @Override public void registerAdapterDataObserver(RecyclerView.AdapterDataObserver observer) { mInnerAdapter.registerAdapterDataObserver(observer); } @Override public void unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver observer) { mInnerAdapter.unregisterAdapterDataObserver(observer); } @Override public void onViewRecycled(RecyclerView.ViewHolder holder) { mInnerAdapter.onViewRecycled(holder); } } /** * ViewHolder of footerView */ private class FooterViewHolder extends RecyclerView.ViewHolder { public FooterViewHolder(View itemView) { super(itemView); } } /** * a inner class used to monitor the dataSet change * <p> * because wrapperAdapter do not know when wrapperAdapter.mInnerAdapter * <p> * dataSet changed, these method are final */ class DataObserver extends RecyclerView.AdapterDataObserver{ @Override public void onChanged() { super.onChanged(); RecyclerView.Adapter adapter = recyclerView.getAdapter(); if(adapter != null && mEmptyView != null){ int count = 0; if(isLoadMoreEnable && adapter.getItemCount() != 0){ count ++; } if(adapter.getItemCount() == count){ isEmptyViewShowing = true; if(mEmptyView.getParent() == null){ LayoutParams params = new LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); params.gravity = Gravity.CENTER; addView(mEmptyView, params); } recyclerView.setVisibility(GONE); mEmptyView.setVisibility(VISIBLE); }else{ isEmptyViewShowing = false; mEmptyView.setVisibility(GONE); recyclerView.setVisibility(VISIBLE); } } mWrapperAdapter.notifyDataSetChanged(); } @Override public void onItemRangeChanged(int positionStart, int itemCount) { super.onItemRangeChanged(positionStart, itemCount); mWrapperAdapter.notifyItemRangeChanged(positionStart, itemCount); } @Override public void onItemRangeChanged(int positionStart, int itemCount, Object payload) { super.onItemRangeChanged(positionStart, itemCount, payload); mWrapperAdapter.notifyItemRangeChanged(positionStart, itemCount, payload); } @Override public void onItemRangeInserted(int positionStart, int itemCount) { super.onItemRangeInserted(positionStart, itemCount); mWrapperAdapter.notifyItemRangeInserted(positionStart, itemCount); } @Override public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) { super.onItemRangeMoved(fromPosition, toPosition, itemCount); mWrapperAdapter.notifyItemRangeRemoved(fromPosition, itemCount); } @Override public void onItemRangeRemoved(int positionStart, int itemCount) { super.onItemRangeRemoved(positionStart, itemCount); mWrapperAdapter.notifyItemRangeRemoved(positionStart, itemCount); } } public interface OnLoadListener { void onRefresh(); void onLoadMore(); } }
<?xml version="1.0" encoding="utf-8"?> <androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/drawer_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" tools:openDrawer="start"> <include layout="@layout/app_bar_main" android:layout_width="match_parent" android:layout_height="match_parent" /> <com.google.android.material.navigation.NavigationView android:id="@+id/nav_view" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="start" android:fitsSystemWindows="true" app:headerLayout="@layout/nav_header_main" app:menu="@menu/activity_main_drawer" /> </androidx.drawerlayout.widget.DrawerLayout>

♻️ 资源

大小:15.9MB

➡️资源下载:https://download.csdn.net/download/s1t16/87404181

注:更多内容可关注微信公众号【神仙别闹】,如当前文章或代码侵犯了您的权益,请私信作者删除!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/23 11:32:38

XXMI启动器完整指南:5分钟学会多游戏模组管理

XXMI启动器完整指南&#xff1a;5分钟学会多游戏模组管理 【免费下载链接】XXMI-Launcher Modding platform for GI, HSR, WW and ZZZ 项目地址: https://gitcode.com/gh_mirrors/xx/XXMI-Launcher 还在为原神、星穹铁道、绝区零等游戏模组管理而烦恼吗&#xff1f;XXMI…

作者头像 李华
网站建设 2026/4/23 14:42:18

游戏模组管理新纪元:XXMI启动器全方位使用手册

游戏模组管理新纪元&#xff1a;XXMI启动器全方位使用手册 【免费下载链接】XXMI-Launcher Modding platform for GI, HSR, WW and ZZZ 项目地址: https://gitcode.com/gh_mirrors/xx/XXMI-Launcher 还在为复杂的MOD安装流程而烦恼吗&#xff1f;XXMI启动器作为专业的游…

作者头像 李华
网站建设 2026/4/17 21:03:38

RimSort终极指南:轻松解决环世界模组管理难题

RimSort终极指南&#xff1a;轻松解决环世界模组管理难题 【免费下载链接】RimSort 项目地址: https://gitcode.com/gh_mirrors/ri/RimSort 还在为《环世界》模组加载冲突而烦恼吗&#xff1f;每次添加新模组都要担心游戏崩溃&#xff1f;RimSort作为一款免费开源的跨平…

作者头像 李华
网站建设 2026/4/23 14:52:22

【Rust日报】 Fjall:日志结构化、可嵌入的键值存储引擎

Fjall&#xff1a;日志结构化、可嵌入的键值存储引擎Fjall 发布了 v3.0&#xff0c;它是一个用纯 Rust 编写的、基于 LSM-tree&#xff08;日志结构合并树&#xff09;的高性能嵌入式键值&#xff08;KV&#xff09;存储引擎。核心定位&#xff1a;Fjall 旨在为 Rust 生态提供一…

作者头像 李华
网站建设 2026/4/23 13:09:14

手把手教你 5 分钟打造属于自己的AI编程智能体!

一、当前编程模式的痛点unsetunset作为一名长期使用 AI 辅助编程的开发者&#xff0c;我发现了一个普遍存在的问题&#xff1a;1.1 传统 AI 对话的局限性除了使用 cursor、Trae、codebuddy 等工具外&#xff0c;在编程环节每次向 ChatGPT、Claude 等 AI 寻求编程帮助时&#xf…

作者头像 李华
网站建设 2026/4/23 13:09:13

NBTExplorer完全指南:零基础学会Minecraft数据可视化编辑

NBTExplorer完全指南&#xff1a;零基础学会Minecraft数据可视化编辑 【免费下载链接】NBTExplorer A graphical NBT editor for all Minecraft NBT data sources 项目地址: https://gitcode.com/gh_mirrors/nb/NBTExplorer 还在为复杂的Minecraft数据编辑而烦恼吗&…

作者头像 李华