关于6-5到6-26日雷鸟管理端项目的总结

来源:互联网 发布:自动关注软件下载 编辑:程序博客网 时间:2024/04/30 07:19

雷鸟管理端项目总结(Android)

              这个项目是我在深圳悠悠乐科技有限公司完成的第二个项目,该项目主要是实现当前团队所做加速器项目的Android管理端,方便项目小组成员对项目的掌控。
通过这个项目我学习到了很多, 主要是在fragment,activity的界面交互绘制和布局多层嵌套的使用收获了很多,对Android布局的设计能力有了进一步的提升,还有就是在网络异步线程上的网络数据获取和获取数据之后加载数据的使用有了很大的进步。最重要的是,我对整个开发的流程和在开始开发程序时的一些框架设计有了非常的大收获,相比于第一个天气预报的项目,由于框架没设计好,导致代码的复用性很差到这一次框架上的设计减少了很多无用功,提高了很多效率。我进步了,但是很多地方的设计扩展性还不够,我觉得这是我在这个项目里除了代码积累外最大的提升。
接下来,我就详细的说下自己在本项目中的不足与收获。

目录

第一章   界面整体框架的设计——Fragment,TableHost + ViewPager实现页面高效的左右滑动
第二章 数据分析图表显示——开源库MPAndroidChart的使用
第三章 少用但是存在的需求——ScrollView和ListView的嵌套
第四章 仿微信聊天界面的对话显示——使用listView实现
第五章 其它小模块的说明
第六章 总结

第一章   界面整体框架的设计——Fragment,TableHost + ViewPager实现


现如今Fragment,TableHost和Viewpager的组合使用已经非常常见了,现在中国人都离不开的微信就是使用
这三种控件实现的界面框架。为什么这种组合会这么受欢迎呢,用View代替Fragment一般情况下也是可以实现功能
的,为什么不用View呢,下面我来说说我的想法,由于我的水平有限,可能有一些错误,欢迎大家指正。
1、为什么选择Fragment作为界面的显示,而不使用View。因为Fragment拥有比Activity更多的生命周期,能够
满足更多人对于界面切换时资源的配置和相应的处理,合理使用可以让用户有更好的体验。
2、使用tableHost,viewPager是为了让用户能够通过点击和手势滑动快速定位到自己想使用的功能,给用户一个
更好的使用体验。
上面讲述了为什么要使用这个组合的原因,下面我们就来说下这三个组合的具体功能怎么去实现。

步骤一:准备好想对应的fragment图片,文字以及fragment类;
步骤二:创建布局文件,包含TableHost,ViewPager和Fragment,如下图所示。

<?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="match_parent"    android:background="@color/background_color"    android:orientation="vertical">    <LinearLayout        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:gravity="center_horizontal"        android:background="@color/backgroud_title"        android:orientation="horizontal"        android:padding="8dp">        <ImageView            android:id="@+id/main_titleicn_iv"            android:layout_width="30dp"            android:layout_height="30dp"            android:src="@drawable/small_icon" />        <TextView            android:id="@+id/main_titleText_tv"            android:layout_width="wrap_content"            android:layout_height="30dp"            android:gravity="center"            android:text="@string/app_name"            android:textColor="@android:color/background_light"            android:textSize="20sp" />    </LinearLayout>    <android.support.v4.view.ViewPager        android:id="@+id/main_viewPager_vp"        android:layout_width="match_parent"        android:layout_height="0dp"        android:layout_weight="1" />    <View  android:layout_height="1dp"        android:layout_width="match_parent"        android:background="#fff0f0f0"        />    <android.support.v4.app.FragmentTabHost        android:id="@+id/main_tabHost_th"        android:layout_width="match_parent"        android:layout_height="wrap_content">        <FrameLayout            android:id="@android:id/tabcontent"            android:layout_width="0dp"            android:layout_height="0dp"            android:layout_weight="0" />    </android.support.v4.app.FragmentTabHost></LinearLayout>

步骤 三:代码部分初始化TableHost,ViewPager和Fragment。

//初始化和设置监听器vp = (ViewPager) findViewById(R.id.main_viewPager_vp);vp.addOnPageChangeListener(this);//数据初始化fragmentList = new ArrayList<>();basicInfoFragment = new BasicInfoFragment(this);serverFragment = new ServerFragment(this);userFragment = new UserFragment(this);infringeFragment = new InfringeFragment(this);analysisFragment = new AnalysisFragment(this);fragmentList.add(basicInfoFragment);fragmentList.add(serverFragment);fragmentList.add(userFragment);fragmentList.add(infringeFragment);fragmentList.add(analysisFragment);//控件初始化并且和viewPager绑定tabHost = (FragmentTabHost) findViewById(R.id.main_tabHost_th);tabHost.setup(this, getSupportFragmentManager(), R.id.main_viewPager_vp);//绑定viewpager

步骤 四:为viewPager创建适配器,为TabHost填充做好准备

//tabhost
private View getTabItemView(int i) {    LayoutInflater layoutInflater = LayoutInflater.from(this);    View view = layoutInflater.inflate(R.layout.tab_content, null);    imageArray[i] = (ImageView) view            .findViewById(R.id.tab_imageView_iv);    //设图片看起来更适应    if (i == 0 || i == 1 || i == 4) {        DisplayMetrics dm = new DisplayMetrics();        getWindowManager().getDefaultDisplay().getMetrics(dm);        if (DEBUG) LogUtil.d(TAG, "getTabItemView: dm.density = " + dm.density);        ViewGroup.LayoutParams layoutParams = imageArray[i].getLayoutParams();        imageArray[i].getMeasuredHeight();        layoutParams.height = (int) (32 * dm.density);//设置图片的高度        layoutParams.width = (int) (32 * dm.density); //设置图片的宽度        imageArray[i].setLayoutParams(layoutParams);        LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(imageArray[i].getLayoutParams());        lp.setMargins((int) (1.5 * dm.density), (int) (0 * dm.density), (int) (1.5 * dm.density), (int) (3 * dm.density));        imageArray[i].setLayoutParams(lp);    }    Drawable icon = getResources().getDrawable(tabImageViewArray[i]);    Drawable tintIcon = DrawableCompat.wrap(icon);    DrawableCompat.setTintList(tintIcon, getResources().getColorStateList(R.color.wlwx_tab_color));    imageArray[i].setImageDrawable(tintIcon);    TextView mTextView = (TextView) view.findViewById(R.id.tab_textView_tv);    mTextView.setText(tabTextViewArray[i]);    return view;}
//Viewpager子类
public class FragmentVPAdapter extends FragmentPagerAdapter {    private List<Fragment> mfragmentList;    public FragmentVPAdapter(FragmentManager fm, List<Fragment> fragmentList) {        super(fm);        mfragmentList = fragmentList;    }    @Override    public Fragment getItem(int position) {        return mfragmentList.get(position);    }    @Override    public int getCount() {        return mfragmentList.size();    }}

步骤 五:为viewPager和Tabhost填充fragment,这里需要注意的是,不能在两个地方都添加fragment,否则会导致
添加两个fragment导致内存泄漏和一些未知异常。

//数据初始化,相当于和适配器和一起设置了for (int i = 0; i < fragmentArray.length; ++i) {    TabHost.TabSpec tabSpec = tabHost.newTabSpec(tabTextViewArray[i]).setIndicator(getTabItemView(i));    tabHost.addTab(tabSpec, NullFragment.class, null);    tabHost.setTag(i);    tabHost.getTabWidget().getChildAt(i)            .setBackgroundResource(R.drawable.selector_tab_color);//设置Tab被选中的时候颜色改变}//设置适配器FragmentVPAdapter fragmentVPAdapter = new FragmentVPAdapter(getSupportFragmentManager(), fragmentList);vp.setAdapter(fragmentVPAdapter);vp.setOffscreenPageLimit(fragmentList.size() - 1);

步骤 六:为viewPager和Tabhost设置监听,使两者同步响应。

/**     * ViewPager监听函数,三个     */    //表示在前一个页面滑动到后一个页面的时候,在前一个页面滑动前调用的方法(调用次数很多,适合做精确处理)    @Override    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {    }    //position是表示你当前选中的页面位置Postion,这事件是在你页面跳转完毕的时候调用的。    @Override    public void onPageSelected(int position) {    }    //state ==1的时候表示正在滑动,state==2的时候表示滑动完毕了,state==0的时候表示什么都没做,就是停在那。    @Override    public void onPageScrollStateChanged(int state) {        if (tabHost.getCurrentTab() == vp.getCurrentItem()) return;        TabWidget widget = tabHost.getTabWidget();        int oldFocusability = widget.getDescendantFocusability();        widget.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);        tabHost.setCurrentTab(vp.getCurrentItem());        widget.setDescendantFocusability(oldFocusability);        if (state == 0) {            //更新数据操作        }    }    /**     * tabHost改变的监听函数     *     * @param tabId     */    @Override    public void onTabChanged(String tabId) {        int position = tabHost.getCurrentTab();        vp.setCurrentItem(position);    }

第二章 数据分析图表显示——开源库MPAndroidChart的使用

开源库MPAndroidChart是一个Android的图表开源库,功能很强大,不仅实现了多种功能样式的图表,
而且图表还支持很多类型事件的监听,功能十分强大,如果你没有太多的时间去自定义一个图表控件,那么
MPAndroidChart绝对是一个很好的选择。
那么怎么去使用MPAndroidChart呢:

步骤一:加载开源库或者下载jar包。这是github的连接https://github.com/PhilJay/MPAndroidChart
如果是使用AndroidStudio进行开发的

allprojects {repositories {maven { url "https://jitpack.io" }}}
dependencies {compile 'com.github.PhilJay:MPAndroidChart:v3.0.2'}
在build.gradle中加入依赖关系,然后让gradle自动加载即可。
如果你使用的是eclipse+JDK+AndroidSDK+ADT做的开发,那么可以选择下载jar包。
如果这个开源库的本身不能满足你的需求,你需要修改源代码来实现你的功能的话,可以选择下载module模块。

步骤二:有了代码之后就是怎么去使用了,首先选择你想要的图表样式,然后在xml文件中定义对应的图表
控件。比如我选择的是HorizontalBarChart(水平条形图)

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="vertical"    android:layout_width="match_parent"    android:layout_height="wrap_content">    <com.github.mikephil.charting.charts.HorizontalBarChart        android:id="@+id/new_tatalUserNum_chart"        android:layout_width="match_parent"        android:layout_height="200dp" />    <com.github.mikephil.charting.charts.HorizontalBarChart        android:id="@+id/new_tatalUserPercent_chart"        android:layout_width="match_parent"        android:layout_height="@dimen/chartHigh" /></LinearLayout>

步骤三:为图表传入数据源,由于数据源的传入方式和需求还有MPAndroidChart的版本有关,所以这里不对个
例进行说明。

步骤四:设置图表样式

barChart.setData(bardata);barChart.getLegend().setPosition(Legend.LegendPosition.ABOVE_CHART_LEFT);//设置注解的位置在左上方barChart.getLegend().setForm(Legend.LegendForm.CIRCLE);//这是左边显示小图标的形状barChart.getLegend().setTextSize(15f);
barChart.getXAxis().setPosition(XAxis.XAxisPosition.BOTTOM);//设置X轴的位置barChart.getXAxis().setDrawGridLines(false);//不显示网格barChart.getXAxis().setTextSize(13f);barChart.setX(20f);barChart.getAxisRight().setEnabled(false);//右侧不显示Y轴barChart.getAxisLeft().setAxisMinValue(0.0f);//设置Y轴显示最小值,不然0下面会有空隙barChart.getAxisLeft().setDrawGridLines(false);//不设置Y轴网格barChart.setDescription("各平台付费率");//设置描述barChart.setDescriptionTextSize(15f);//设置描述字体barChart.animateXY(1000, 2000);//设置动画
这是对图表的一些设置。

第三章 少用但是存在的需求——ScrollView和ListView的嵌套

其实这两者的嵌套是一个很奇怪的组合,但是像淘宝,京东等等数据较多,样式较为复杂的app来说,这
两者的嵌套实际上是很常见的。
但是直接只是在xml文件中嵌套了两者,如下:
<ScrollView    android:id="@+id/basic_scrollView_sv"    android:layout_width="match_parent"    android:layout_height="0dp"    android:layout_weight="1"    android:overScrollMode="never"    android:scrollbars="none">    <ListView        android:layout_width="match_parent"        android:layout_height="match_parent">    </ListView></ScrollView>
你会发现listView只会显示一个item的高度,并且无法拖住滑动到其它的item,这是一个很严重的问题,不是
我们想要的功能。那么怎么办呢,改用其它布局然后组合实现有可能也可以实现功能。但是如果一定要用这种组合
的话怎么办呢,也不是没有办法的。首先,我们思考到现在有两个地方不是我们想要的,一个是ListView没有办法
滑动,第二个是Listview的高度,只要把这两个问题解决了,那我们的需求也就达到了。
下面我们分别来分析和解答两个问题:
1、listview无法滑动:首先我们可以很直观的看到lsitview是scrollview的子布局,通过操作我们发现scrollview
的响应是正常的,那么这个时候我们就应该想到listview做为子布局,他的Touch事件是不是可能被父布局scrollview
截取了呢,我们加入以下代码:
listView.setOnTouchListener(new View.OnTouchListener() {    // Setting on Touch Listener for handling the touch inside ScrollView    @Override    public boolean onTouch(View v, MotionEvent event) {        // Disallow the touch request for parent scroll on touch of child view        v.getParent().requestDisallowInterceptTouchEvent(true);        return false;    }});
对listview进行设置,禁止父布局截断touch事件,重新运行,发现有效,问题一解决。
     2、listview的高度问题:通过查阅源码和资料我们发现,由于scrollview的效果屏蔽了listview的高度
绘制机制,所以listview只有一个item的高度,我们可以通过在布局xml文件中设置固定高度来来改变listview
的显示高度,但是那样太不灵活了,数据过多过少的时候显示都会有问题,在不同屏幕上显示也会出现问题。
那么这就需要我们在界面绘制的时候手动添加代码去修改listview的大小和高度了。
基本思路是这样的,首先获取屏幕的绝对高度和宽度,然后获取想要让listview嵌套在里面的上一个布局
和下一个布局的坐标位置,这样listview的高度和宽度值就可以知道了,然后我们把他重新绘制就好了。
具体代码和说明如下:
private void setFuzzyFeedbackLayout() {    /**     * 设置用户详细信息的显示的布局的宽高     */    //定义DisplayMetrics 对象    DisplayMetrics dm = new DisplayMetrics();    //取得窗口属性    getActivity().getWindowManager().getDefaultDisplay().getMetrics(dm);    //窗口的宽度    int screenWidth = dm.widthPixels;    //窗口高度    int screenHeight = dm.heightPixels;    if (DEBUG)        LogUtil.d(TAG, "initControl: screenWidth = " + screenWidth + "screenHeight = " + screenHeight);    int[] location = new int[2];    //useradd    userAddLLView.getLocationOnScreen(location);    int x = location[0];    int y = location[1];    if (DEBUG) LogUtil.d(TAG, "initControl: x = " + x + "y = " + y);    y += userAddLLView.getHeight();    if (DEBUG) LogUtil.d(TAG, "initControl: x = " + x + "y = " + y);    y += tabHigh;    if (DEBUG) LogUtil.d(TAG, "initControl: x = " + x + "y = " + y);    //在代码中设置控件大小的方法    ViewGroup.LayoutParams lp = listview.getLayoutParams();    currentWidth = lp.width = screenWidth;    currentHeight = lp.height = screenHeight - y;    if (DEBUG)        LogUtil.d(TAG, "initControl: lp.width = " + lp.width + "lp.height = " + lp.height);    userFeedBackFuzzyView.setLayoutParams(lp);}
这里需要注意的几个点是:
1、控件的宽高只有当其绘制完成之后才能得到准确的值,在未绘制完成之前获取到的值为零,这就要
求我们在编写代码时重绘listview宽高的位置需要在其它相关控件绘制完成之后,而且最好不要影响到用户的体
验,不要在用户操作时绘制。
2、由于listview的绘制是动态的,如果与他宽高计算有关的控件的位置发生改变时,如果这个时候重绘
是会导致listview发生变化的,这就提醒我们要完全掌握好listview宽高重绘的时间,以避免发生未知不好的情
况,给用户带来不好的体验。

第四章 仿微信聊天界面的对话显示——使用listView实现

由于本项目中要实现一个微信反馈和客户端用户反馈的即时回复和交流功能,所以在考虑如何较好的实现
这种交互上选择了使用仿微信的方式。
上面的两个图片就是实现功能后的界面,左边的是用户反馈梗概,又边是用户反馈的详情。
接下来我就来说说以上界面及功能的实现:
首先简单说下左边界面的实现:
一个自定义的title + RadioGroup + listview的实现,总的来说还是比较简单的,所以这边只简单说明
步骤一:总布局文件xml定义和listview的item布局文件的定义
步骤二: 绘制listview(1、准备数据源存储容器,2、创建对应的SimpleAdapter,3、listview设置2中
的适配器,4、给listview设置监听)由于我们的数据一般都是网络获取的(1、更新数据源,2、适配器notifyChange
)这样listview就绘制好了。
步骤三:对radioGroup进行监听,在切换选中的RadioButton的时候对listview进行改变。
这样左边界面就实现完成了,接下来我们说下重点的右边界面的实现:
步骤一:总布局文件的绘制,这里listview的item的绘制有点不同,因为需要分别在左右两边显示,所以
需要有两个对应的布局文件。
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="fill_parent"    android:layout_height="wrap_content"    android:orientation="vertical"    android:padding="6dp">    <LinearLayout        android:layout_width="fill_parent"        android:layout_height="wrap_content"        android:gravity="center_horizontal"        android:orientation="vertical">        <TextView            android:id="@+id/tv_sendtime"            style="@style/chat_text_date_style"            android:layout_width="wrap_content"            android:layout_height="wrap_content" />    </LinearLayout>    <RelativeLayout        android:layout_width="fill_parent"        android:layout_height="wrap_content"        android:layout_marginTop="5dp">        <ImageView            android:id="@+id/iv_userhead"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_alignParentLeft="true"            android:layout_alignParentTop="true"            android:src="@drawable/user_menu"            android:focusable="false"            android:tint="@color/blue" />        <TextView            android:id="@+id/tv_chatcontent"            style="@style/chat_content_date_style"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_marginLeft="10dp"            android:layout_toRightOf="@id/iv_userhead"            android:background="@drawable/chatfrom_bg" />        <TextView            android:id="@+id/tv_username"            style="@style/chat_text_name_style"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_alignParentLeft="true"            android:layout_below="@id/iv_userhead"            android:layout_toLeftOf="@id/tv_chatcontent" />    </RelativeLayout></LinearLayout>

步骤二:创建对应的Adapter,由于AndroidSDK提高的几种现成的Adapter无法实现我们想要的效果,所以
我们这里自己创建一个新的Adapter并基础BaseApapter:
public class FeedbackViewAdapter extends BaseAdapter {    private List<ChatMsgEntity> data;    private Context context;    private LayoutInflater mInflater;    public FeedbackViewAdapter(Context context, List<ChatMsgEntity> data) {        this.context = context;        this.data = data;        this.mInflater = LayoutInflater.from(context);    }    public int getCount() {        return this.data.size();    }    public Object getItem(int position) {        return this.data.get(position);    }    public long getItemId(int position) {        return (long)position;    }    public int getItemViewType(int position) {        ChatMsgEntity entity = (ChatMsgEntity)this.data.get(position);        return entity.getMsgType()?0:1;    }    public int getViewTypeCount() {        return 2;    }    public View getView(int position, View convertView, ViewGroup parent) {        ChatMsgEntity entity = (ChatMsgEntity)this.data.get(position);        boolean isComMsg = entity.getMsgType();        FeedbackViewAdapter.ViewHolder viewHolder = null;        if(convertView == null) {            if(isComMsg) {                convertView = this.mInflater.inflate(R.layout.chatting_item_msg_text_left, (ViewGroup)null);            } else {                convertView = this.mInflater.inflate(R.layout.chatting_item_msg_text_right, (ViewGroup)null);            }            viewHolder = new FeedbackViewAdapter.ViewHolder();            viewHolder.tvSendTime = (TextView)convertView.findViewById(R.id.tv_sendtime);            viewHolder.tvUserName = (TextView)convertView.findViewById(R.id.tv_username);            viewHolder.tvContent = (TextView)convertView.findViewById(R.id.tv_chatcontent);            viewHolder.isComMsg = isComMsg;            convertView.setTag(viewHolder);        } else {            viewHolder = (FeedbackViewAdapter.ViewHolder)convertView.getTag();        }        viewHolder.tvSendTime.setText(entity.getDate());        viewHolder.tvUserName.setText(entity.getName());        viewHolder.tvContent.setText(entity.getText());        return convertView;    }    public interface IMsgViewType {        int IMVT_COM_MSG = 0;        int IMVT_TO_MSG = 1;    }   class ViewHolder {        public TextView tvSendTime;        public TextView tvUserName;        public TextView tvContent;        public boolean isComMsg = true;    }}
对应的消息类的封装
public class ChatMsgEntity {    private static final String TAG = ChatMsgEntity.class.getSimpleName();    private String name;    private String date;    private String text;    private boolean isComMeg = true;    public String getName() {        return this.name;    }    public void setName(String name) {        this.name = name;    }    public String getDate() {        return this.date;    }    public void setDate(String date) {        this.date = date;    }    public String getText() {        return this.text;    }    public void setText(String text) {        this.text = text;    }    public boolean getMsgType() {        return this.isComMeg;    }    public void setMsgType(boolean isComMsg) {        this.isComMeg = isComMsg;    }    public ChatMsgEntity() {    }    public ChatMsgEntity(String name, String date, String text, boolean isComMsg) {        this.name = name;        this.date = date;        this.text = text;        this.isComMeg = isComMsg;    }}
步骤三:listview设置适配器
步骤四:lsitview设置监听器
步骤五:从网络获取数据之后传入转化成带有左右标识的数据源传入,通知适配器数据改变。

第五章 其它小模块的说明

在这一章的话我会不定期更新,如过感觉有的模块比较重要,可能会另起一章,一般就是在本章添加一些觉得
有必要的小功能的实现。
1、网络异步线程(get,post方式):由于在本人别的博客里有说明过了,这里就不再重复了。

2、获取当前时间:

// 获取当前的年、月、日、小时、分钟Calendar c = Calendar.getInstance();//转成UTC时间,和服务器统一int zoneOffset = c.get(java.util.Calendar.ZONE_OFFSET);int dstOffset = c.get(java.util.Calendar.DST_OFFSET);c.add(java.util.Calendar.MILLISECOND, -(zoneOffset + dstOffset));
对获取的时间进行加天数或者减天数
c.add(Calendar.DAY_OF_MONTH, -1);c.add(Calendar.MONTH, 1);
获取时间
int hour = c.getHour();

3、Spinner的使用:

步骤一:在布局文件中定义Spinner控件
步骤二:给Spinner提高数据源,这里有两种方式
1)android:entries方式获取静态数据源,这中方式适合Spinner的数据源是固定不变的情况
<android.support.v7.widget.AppCompatSpinner    android:id="@+id/basic_modeSelector_sp"    android:layout_width="match_parent"    android:layout_height="wrap_content"    android:entries="@array/basic_spinner" />
2)通过动态绑定适配器的方式,做Android开发一定时间了就会发现,其实这是Android特别常见的一种
控件绑定数据的方式,简单而且好用,步骤总是那么几步,但是可以实现我们各种丰富多彩的功能。
3)设置监听器,2,3的具体操作我就不说了,和listview什么的都很类似,算是基本操作了。

4、输入自动补全实现——使用AutoCompleteTextView控件:

步骤一:在xml文件中定义控件
<AutoCompleteTextView    android:id="@+id/infringe_ip_et"    android:layout_width="0dp"    android:layout_height="50dp"    android:layout_gravity="center"    android:layout_weight="1"    android:textColor="@color/white"    android:hint="手动输入IP" />
步骤二:创建适配器(实质上就是adapter和数据源建立一个绑定关系)
步骤三:控件绑定适配器(实质上就是控件和adapter的绑定)
通过步骤二和步骤三adapter的中转,从而达到了控件和数据源的绑定。
步骤四:为控件设置监听器。

5、实现双击退出:

这是现在软件很常用的一个功能,实现起来时间上也很简单,主要有以下两种实现方式:
方法一:使用延迟线程private boolean isBackBtnPressed = false;@Overridepublic void onBackPressed() {    if (isBackBtnPressed) {        finish();    } else {        Toast.makeText(this, "再按一次退出", Toast.LENGTH_SHORT).show();        isBackBtnPressed = true;        new Timer().schedule(new TimerTask() {            @Override            public void run() {                isBackBtnPressed = false;            }        }, 2000);    }}
方法二:使用两次点击获取系统的时间差的方式
private long lastTime = 0;@Overridepublic void onBackPressed() {    if (DEBUG) LogUtil.d(TAG, "onBackPressed: vp.getCurrentItem() = " + vp.getCurrentItem());    if (vp.getCurrentItem() == 0) {        basicInfoFragment.closeProgress();    }    long time = System.currentTimeMillis();    if (time - lastTime > 2000) {        Toast.makeText(this, "再按一次返回登录", Toast.LENGTH_SHORT).show();        lastTime = time;    } else {        finish();    }}
这里一般推荐使用方式二,因为方式一会产生线程,而这并不是一个耗时的操作,重复多次一直这样创建
线程比较容易导致一些未知的问题。

6、登录界面实现自动登录、记住密码

步骤一:首先同样是创建相应的布局文件:
<LinearLayout    android:layout_width="match_parent"    android:layout_height="40dp">    <LinearLayout        android:layout_width="0dp"        android:layout_height="wrap_content"        android:layout_weight="1"        android:orientation="horizontal">        <CheckBox            android:id="@+id/login_remember_cb"            android:layout_width="wrap_content"            android:layout_height="wrap_content" />        <TextView            android:id="@+id/login_remember_tv"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="记住密码"            android:textColor="#ffffff" />    </LinearLayout>    <LinearLayout        android:layout_width="0dp"        android:layout_height="wrap_content"        android:layout_weight="1"        android:orientation="horizontal">        <CheckBox            android:id="@+id/login_auto_cb"            android:layout_width="wrap_content"            android:layout_height="wrap_content" />        <TextView            android:id="@+id/login_auto_tv"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="自动登录"            android:textColor="#ffffff" />    </LinearLayout></LinearLayout>
步骤二:设置相应的监听和事件的处理,这里需要注意的是,存在一个逻辑文。选中自动登录的时候,记
住密码需要也默认选中,而取消记住密码的时候,自动登录的选中应该也被取消。
步骤三:在这个Activity的onDestroy()方法中用SharedPreferences.edit保存记录的账号和密码,还有自
动登录和记住密码的状态,然后再下次OnCreate的时候使用SharePreferences读取出上次推出时的状态,根据记住
密码和自动登录被选择的状态加载相应的数据。

7、登录界面背景图的动态显示:

步骤一:导入android-gif-drawable包
compile 'pl.droidsonroids.gif:android-gif-drawable:1.2.7'
步骤二:定义gifImageView控件,设置android:background为想要的背景图
<pl.droidsonroids.gif.GifImageView    android:layout_width="match_parent"    android:layout_height="match_parent"    android:background="@drawable/login_bg2"></pl.droidsonroids.gif.GifImageView>

8、tint的使用:tint的本质是一个图像与或非处理的工具,他可以根据你选择的处理模式处理你想要

处理的图片,然后等到想要的结果。tint最常用的时候是一个背景图需要显示不同颜色的场景。如果不使用tint,
那么你可能需要去找同一张图片有不同颜色的情况,这中方式不仅不仅麻烦,还占内存,不推荐使用。
那么tint怎么使用呢,主要也是有两种方式:
1)在布局文件要使用tint处理的控件中加入,这中静态的方式合适颜色不需要动态改变的地方:
<ImageView    android:layout_width="35dp"    android:layout_height="35dp"    android:layout_gravity="center_vertical"    android:padding="8dp"    android:src="@drawable/user_menu"    android:tint="@color/red" />
2)在代码中动态加入,如果某个地方的背景图颜色需要根据不同的选择情况改变的话,就可以使用这
种方式:
Drawable icon = getResources().getDrawable(tabImageViewArray[i]);Drawable tintIcon = DrawableCompat.wrap(icon);DrawableCompat.setTintList(tintIcon, getResources().getColorStateList(R.color.wlwx_tab_color));imageArray[i].setImageDrawable(tintIcon);

9、8讲完之后,我们就不得不在说到一个东西,selector,上

<?xml version="1.0" encoding="utf-8"?><selector xmlns:android="http://schemas.android.com/apk/res/android">    <item android:state_selected="true" android:color="@color/table_sel"/>    <item android:state_focused="true" android:color="@color/table_sel"/>    <item android:state_pressed="true" android:color="@color/table_sel"/>    <item android:color="@color/table"/></selector>
上面就是8中的R.color.wlwx_tab_color 的内容。
根据selector,我们可以设置一个控件在普通,被选中,被聚焦,被按住时的操作,非常好用。

10、Intent中传播对象,主要有两种方式

Android中Intent传递对象有两种方式一种是通过实现Serializable接口传递对象,一种是通过实现Parcelable接口传递对象。
Serializable是Java提供的序列化接口,而Parcelable的android团队设计的,两者各有利弊。Serializable的实现简单,用起来有
点像gson,而Parcelable的实现相对复杂,要重写很多函数,不够胜在效率高。
所以,在内存中的序列化例如Intent传递推荐Parcelable,存储到设备或者进行网络传输推荐Serializable。

11、未完待续。。。

第六章 总结

这一次的项目让我收获了很多,一方面:我的代码经验上,库函数,不同函数的使用上,以及看到函数
能够想到他底层实现的能力上和解决问题的能力上,我都增长了很多。更重要的另一方面:我对代码的健壮性和
扩展性有了更深入的了解,我明白了一个好的代码,好的工程,目前功能的完好实现是基础,还有很重要的一点
是代码的健壮性一个要好,当以后因为需要要添加一个功能,或者网络接口的数据改变是,没有必要去改大量的
代码,最后是能够直接通过去实现原本app的接口来实现功能,这是最好的,能够极大的提升效率,也能避免因为
程序原本设计员和原本的修改者不同而导致改代码是可能引起的出现bug的情况。
从4.12到今天6.27,两个半月除去中间回学校20天左右的时间,自己一直在努力,也进步了很多,不过
现在自己的水平仍然很差,但是相信自己能够慢慢进步,一直到成为高手的,加油!!
下写自己的最近的经验与此刻的心情,与君共勉!
原创粉丝点击