APP开发流程实例讲解-儒释道网络电台八天开发全程-在Android Studio中完成界面设计
来源:互联网 发布:淘宝短视频服务商 编辑:程序博客网 时间:2024/06/05 04:35
APP开发流程实例讲解-儒释道网络电台八天开发全程
功能和界面初步设定
APP开发流程实例讲解-儒释道网络电台八天开发全程
- 项目发起
- 功能和界面初步设定
- 在Android Studio中完成界面设计
- 实现功能代码:播放控制
- 优化排错:增强稳定性和添加异常处理
- 界面美化并进一步优化排错
- 百度云深度兼容测试并进一步优化排错
- 签名发布
昨天的做的设计图是比较简单的,主要麻烦是需要实现两侧的滑动抽屉菜单。
在Android Studio中有一个模板可以创建左侧抽屉,但儒释道网络电台APP需要两边两个抽屉。在网上找到一篇文章《AndroidDrawerLayout+fragment布局实现左右侧滑 》,是使用FragmentTransaction来实现左右侧栏的显现。还有一种办法是使用第三方组件SlidingMenu。难道就不能用Android Studio模板可以创建两侧抽屉滑动菜单吗?经过我一个多小时的探索和尝试,最终发现是可以的。创建的方法我已经写到另一篇文章《Android DrawerLayout+NavigationView布局实现左右两边侧滑菜单 》,这里不再多说。今天主要的完成的工作有。
双侧滑动抽屉菜单
实现方法上面已经说过,但又略有不同。根据昨天设计的界面,两侧菜单其实是三个ListView,但NavigationView主要是结合menu菜单来创建,有些不对路。经过尝试,最终删掉了NavigationView的menu属性,将列表加入到headerLayout中。实际代码如下:
nav_header_main.xml文件
<?xml version="1.0" encoding="utf-8"?><android.support.v4.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" /> <android.support.design.widget.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" /> <android.support.design.widget.NavigationView android:id="@+id/right_nav_view" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="end" android:fitsSystemWindows="true" app:headerLayout="@layout/nav_server_header_main" /></android.support.v4.widget.DrawerLayout>然后就是在两个header的Layout文件如下:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:showDividers="end" android:divider="@drawable/bottom_line"> <ImageView android:id="@+id/imageView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingTop="@dimen/nav_header_vertical_spacing" android:src="@mipmap/ic_launcher" android:padding="8dp" /> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="bottom" android:paddingBottom="8dp"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/app_name" /> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="android.studio@android.com" /> </LinearLayout> </LinearLayout> <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="@dimen/banner48" android:showDividers="end" android:divider="@drawable/bottom_line" android:gravity="center_vertical"> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@android:drawable/ic_menu_today" android:layout_margin="8dp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceMedium" android:text="节目列表" /> </LinearLayout> <ListView android:id="@+id/lv_programs" android:layout_width="match_parent" android:layout_height="wrap_content"> </ListView> <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="@dimen/banner48" android:showDividers="end" android:divider="@drawable/bottom_line" android:gravity="center_vertical"> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@android:drawable/ic_menu_compass" android:layout_margin="8dp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceMedium" android:text="最新消息" /> </LinearLayout> <ListView android:id="@+id/lv_news" android:layout_width="match_parent" android:layout_height="wrap_content"> </ListView></LinearLayout>
nav_server_header_main.xml
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="@dimen/banner48" android:showDividers="end" android:divider="@drawable/bottom_line" android:gravity="center_vertical"> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@android:drawable/ic_menu_compass" android:layout_margin="8dp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceMedium" android:text="选择线路" /> </LinearLayout> <ListView android:id="@+id/lv_servers" android:layout_width="match_parent" android:layout_height="wrap_content"> </ListView></LinearLayout>
ListView列表项Layout就不贴了。
主界面的设计
如昨天设计图所示的Layout文件如下:
<?xml version="1.0" encoding="utf-8"?><RelativeLayout 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:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:context="com.jianchi.fsp.buddhismnetworkradio.MainActivity" tools:showIn="@layout/app_bar_main"> <!--640*360--> <FrameLayout android:id="@+id/player_frame" android:layout_alignParentStart="true" android:layout_alignParentTop="true" android:layout_alignParentLeft="true" android:layout_width="match_parent" android:layout_height="240dp"> <VideoView android:layout_width="match_parent" android:layout_height="240dp" android:id="@+id/videoView" /> <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="@dimen/banner48" android:layout_gravity="bottom" android:background="#b4b4b4b4" android:gravity="center_vertical" android:padding="8dp"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="净土大经科注 第318集" android:id="@+id/textView3" android:textColor="@android:color/white" android:layout_weight="1" /> <CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="仅声音" android:id="@+id/checkBox" /> </LinearLayout> </FrameLayout> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@+id/player_frame" android:layout_alignParentLeft="true" android:layout_alignParentBottom="true"> <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:background="@drawable/side_nav_bar" android:layout_height="@dimen/banner48" android:gravity="center_vertical"> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@mipmap/ic_jiangyi" android:paddingLeft="8dp" android:paddingRight="8dp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="经文讲义" /> </LinearLayout> <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="center_horizontal"> <ScrollView android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/scrollView" android:padding="8dp"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text New Text " android:id="@+id/textView2" /> </ScrollView> </LinearLayout> </LinearLayout></RelativeLayout>
根据比例设置视频窗口高度
FrameLayout player_frame = (FrameLayout) findViewById(R.id.player_frame); WindowManager wm = this.getWindowManager(); int width = wm.getDefaultDisplay().getWidth(); int height = width * 9 / 16; player_frame.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, height));
初步完成网络访问API类
网络访问使用了OKHTTP组件,并且是异步访问。OKHttp的使用这里就不多说了。功能实现方法是使用OKHttp下载网页,用正则表达式解析网页,抓取数据生成对象。调用完成事件将数据传回。然后再在UI线程中使用数据填充ListView等。
API类如下
package com.jianchi.fsp.buddhismnetworkradio;import java.io.IOException;import java.util.ArrayList;import java.util.List;import java.util.regex.Matcher;import java.util.regex.Pattern;import okhttp3.Call;import okhttp3.Callback;import okhttp3.OkHttpClient;import okhttp3.Request;import okhttp3.Response;/** * Created by fsp on 16-7-5. */public class WebApi { /* 节目时间表: 视频音频播放器 线路选择:http://www.amtb.tw/tvchannel/play-1-revised.asp 最新讯息:http://www.amtb.tw/tvchannel/show_marquee.asp 经文讲义:http://ft.hwadzan.com/mycalendar/mycalendar_embed_livetv.php?calendar_name=livetv * */ private static final String programsListUrl = "http://ft.hwadzan.com/mycalendar/mycalendar_embed.php?calendar_name=livetv&showview=day&valign=true&bgcolor=none&showtimecolumns=start&tvmenu=3"; private static final String serversListUrl = "http://www.amtb.tw/tvchannel/play-1-revised.asp"; private static final String newsListUrl = "http://www.amtb.tw/tvchannel/show_marquee.asp"; private static final String noteUrl = "http://ft.hwadzan.com/mycalendar/mycalendar_embed_livetv.php?calendar_name=livetv"; Pattern programsListPattern = Pattern.compile("<td class='style1'>\\s*<strong>(.*?)</strong>\\s*(.*?)\\s*</td>"); Pattern htmlTagPattern = Pattern.compile("<[^>]*>"); Pattern serversListPattern = Pattern.compile("serverAddress = \"(.*?)\";\\s*serverName = \"(.*?)\";"); Pattern newsListPattern = Pattern.compile("<div id='bul_\\d'>(.*?)</div>"); Pattern noteItemPattern = Pattern.compile("<p class=\"MsoNormal\">\\s*(.*?)\\s*</p>"); private OkHttpClient client = new OkHttpClient(); IProgramsListEvent programsListEvent; IServersListEvent serversListEvent; IProgramsListEvent newsListEvent; IStringEvent noteEvent; public void GetNote(IStringEvent noteEvent) { this.noteEvent = noteEvent; Request request = new Request.Builder() .url(noteUrl) .build(); client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { e.printStackTrace(); WebApi.this.noteEvent.getMsg(null); } @Override public void onResponse(Call call, Response response) throws IOException { String html = new String(response.body().bytes(), "big5"); Matcher m = noteItemPattern.matcher(html); StringBuilder sb = new StringBuilder(); while (m.find()){ String nm = m.group(1); if(nm.startsWith("<")) { Matcher hm = htmlTagPattern.matcher(nm); nm = hm.replaceAll(""); } sb.append(nm).append("\r\n"); } WebApi.this.noteEvent.getMsg(sb.toString()); } }); } public void GetNewsList(IProgramsListEvent newsListEvent) { this.newsListEvent = newsListEvent; Request request = new Request.Builder() .url(newsListUrl) .build(); client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { e.printStackTrace(); WebApi.this.newsListEvent.getItems(null); } @Override public void onResponse(Call call, Response response) throws IOException { String html = new String(response.body().bytes(), "big5"); Matcher m = newsListPattern.matcher(html); List<String> newsList = new ArrayList<String>(); while (m.find()){ String nm = m.group(1); if(nm.startsWith("<")) { Matcher hm = htmlTagPattern.matcher(nm); nm = hm.replaceAll(""); } newsList.add(nm); } WebApi.this.newsListEvent.getItems(newsList); } }); } public void GetProgramsList(IProgramsListEvent programsListEvent) { this.programsListEvent = programsListEvent; Request request = new Request.Builder() .url(programsListUrl) .build(); client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { e.printStackTrace(); WebApi.this.programsListEvent.getItems(null); } @Override public void onResponse(Call call, Response response) throws IOException { String html = new String(response.body().bytes(), "utf-8"); Matcher m = programsListPattern.matcher(html); List<String> programsList = new ArrayList<String>(); while (m.find()){ String nm = m.group(2); if(nm.startsWith("<")) { Matcher hm = htmlTagPattern.matcher(nm); nm = hm.replaceAll(""); } programsList.add(m.group(1) + " " + nm); } WebApi.this.programsListEvent.getItems(programsList); } }); } public void GetServersList(IServersListEvent serversListEvent) { this.serversListEvent = serversListEvent; Request request = new Request.Builder() .url(serversListUrl) .build(); client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { e.printStackTrace(); } @Override public void onResponse(Call call, Response response) throws IOException { String html = new String(response.body().bytes(), "utf-8"); Matcher m = serversListPattern.matcher(html); ServersList serverList = new ServersList(); while (m.find()){ serverList.add(new ServerInfo(m.group(1), m.group(2))); } WebApi.this.serversListEvent.getServers(serverList); } }); }}
在UI线程中调用的代码如下
api = new WebApi(); api.GetNewsList(new IProgramsListEvent() { @Override public void getItems(List<String> programs) { runOnUiThread(new Runnable() { @Override public void run() { } }); } }); api.GetNote(new IStringEvent() { @Override public void getMsg(String msg) { runOnUiThread(new Runnable() { @Override public void run() { } }); } }); api.GetProgramsList(new IProgramsListEvent() { @Override public void getItems(List<String> programs) { runOnUiThread(new Runnable() { @Override public void run() { } }); } }); api.GetServersList(new IServersListEvent() { @Override public void getServers(ServersList servers) { runOnUiThread(new Runnable() { @Override public void run() { } }); } });
总的来说写代码用的时间比较少,界面设计不熟悉用时较多。原计划每天花2个小时来做这个APP,但今天实际花有4个多小时。界面代码那里花时间过多了,写JAVA代码反而比较快。
明天按计划,写播放器控制的代码。
所有代码已经上传到GIT:https://code.csdn.net/do168/buddhismnetworkradio
- APP开发流程实例讲解-儒释道网络电台八天开发全程-在Android Studio中完成界面设计
- APP开发流程实例讲解-儒释道网络电台八天开发全程-项目发起
- APP开发流程实例讲解-儒释道网络电台八天开发全程-功能和界面初步设定
- APP开发流程实例讲解-儒释道网络电台八天开发全程-实现功能代码:播放控制
- APP开发流程实例讲解-儒释道网络电台八天开发全程-签名发布
- APP开发流程实例讲解-儒释道网络电台八天开发全程-优化排错:增强稳定性和添加异常处理
- APP开发流程实例讲解-儒释道网络电台八天开发全程-界面美化并进一步优化排错
- APP开发流程实例讲解-儒释道网络电台八天开发全程-百度云深度兼容测试并进一步优化排错
- 在Android Studio中进行NDK开发的一般流程
- 如何在Android Studio中使用Kotlin开发app
- Android APP开发流程
- 实例讲解网站前台界面开发流程
- 【Android】使用Kotlin在Android Studio上开发App
- 【Android】使用Kotlin在Android Studio上开发App
- Android app 基础开发流程
- Android Studio NDK 开发流程
- Android Studio开发JNI流程
- 在Android studio中进行NDK开发
- 什么是用户代理?
- 服务器基础配置(Redhat/CentOS)
- 在数组 arr 的 index 处添加元素 item。不要直接修改数组 arr,结果返回新的数组
- JS Tab切换 选项卡 五种方法
- Fragment的生命周期以及与Activity生命周期联动
- APP开发流程实例讲解-儒释道网络电台八天开发全程-在Android Studio中完成界面设计
- ASP.NET的缓存机制
- 多说 评论系统
- VirtualBox虚拟vdi磁盘扩容
- 愚人的linux内核2440移植札记(超曲折版)
- swift 小知识
- 逆向工程之表达式优化识别(5)-取模
- [UnityShader3]光之翼效果
- App上架流程