基于《第一行代码第二版》的天气app的扩展开发

因为是基于《第一行代码第二版》的天气app的扩展开发,所以就直接使用了书中提到的一些底层依赖库:litepal、okhttp、glide、gson,加上后续功能的开发还用到了recyclerview eventbus databinding,还使用了高德地图api实现了定位功能。天气api依然是使用和风天气,和原书不同的地方在于多用了一个城市搜索的api,用来替换原书中的省市县选择,直接使用输入城市名的方法得到所想查看的城市,这也是和风天气自带的api,具体可查看 https://www.heweather.com/documents/api/v5/search

功能在原书的基础上实现了:1、主界面可以左右滑动切换,看到所有所选城市的天气信息。2、城市管理功能:点击城市,在主界面显示所点击城市的天气信息,长按进行城市的删除。3、搜索城市功能:输入城市名的中文或者拼音可以得到一个或多个城市,点击后可以在主界面生成新页显示天气数据。4、设置界面:可以选择是否自动更新,以及输入自动更新时间。5、首次进入app时,可以下拉定位,显示所在地天气。有数据时下拉可以刷新所有城市的天气信息。





主界面与城市管理与搜索界面的逻辑设计

1.打开app进入MainActivity时,使用litepal数据库得到上次存下的天气信息,如果没有缓存,当使用下拉刷新则使用自动定位、显示刷新圈圈,然后添加litepal数据库并显示天气信息。如果有缓存则使用for循环,添加viewpager页。

2.点击MainActivity中的城市管理按键,进入cityManagementActivity,在这里读取数据库中的所有城市,解析其weather字符串,使用for循环添加item显示到页面上。点击item时用eventbus传递一个刷新的命令,之后结束cityManagementActivity,回到MainActivity中,显示按下城市名的那一页信息。长按item时显示一个删除按键。删除操作,根据position从数组中得到当前按下的item的weatherId,检索数据库进行删除,之后重新加载界面。

3.当点击cityManagementActivity中的添加城市时跳转到SearchCityActivity中,输入城市名的中文或拼音可以得到一个列表,当点击想要查看的城市时,用eventbus发送一个结束与刷新命令,结束SearchCityActivity与cityManagementActivity,并刷新MainActivity中显示的天气信息。
 
关键点:SearchCityActivity得到新城市之后,对litpal进行查询操作,如果不存在将新城市存入litrpal数据库。

Litepal数据库需要记录的值为cityName,weatherId,weather字符串。
 
MainActivity中的swipeRefreshLayout会和viewpager、ScrollView会产生冲突 ,与viewpager的冲突使用 http://blog.csdn.net/u010386612/article/details/50548977#bottom解决,与ScrollView的冲突使 http://blog.csdn.net/qq_16628781/article/details/52025366
解决。

MainActivity中Title的城市名,在首次显示时,直接读取数据库第一位,显示。之后的城市名通过viewpager滑动监听器得到position之后查询数组实现,当切换时可以直接读取显示。

定位功能,在当前数据库不存在数据时,通过下拉刷新得到当地weatherId,刷新界面。
关键功能的代码实现
1、实现对viewpager的添加与信息更新以及小圆点的添加
根据之前逻辑设计的思路,在我们添加城市或者删除城市时,会使用eventbus发送一个refresh来进行更新界面。
@Subscribe(threadMode = ThreadMode.MAIN) //eventbus3.0自带的特性 可以指定在哪个线程中运行
    public void onMoonEvent(MessageEvent event)
    {
        if (event.getMessage().equals("onItemClick"))
        {


            for(int i=0 ; i<cityList.size(); i++)
            {
                if(cityList.get(i).getWeatherId().equals(event.getExtraString()))
                {
                    binding.pager.setCurrentItem(i,false);
                    binding.title.titleCtiy.setText(cityList.get(i).getCityName());
                    break;
                }
            }
        }
        else if(event.getMessage().equals("Refresh"))
        {
            initViewpager(); //这里进行数据库的读取与显示
        }

    }
在每次需要刷新时,会重新读取数据库数据得到所有的城市信息,当weather字符串不为空时,直接显示,为空则联网刷新。
private void initViewpager()
    {

        viewList = new ArrayList<View>();

        cityList.clear();
        binding.title.llContainer.removeAllViews();

        cityList = DataSupport.findAll(City.class);

        if (cityList != null & cityList.size() > 0)
        {
            for (int i = 0; i < cityList.size(); i++)
            {
                final WeatherinfoBinding weatherinfoBinding = DataBindingUtil.inflate(getLayoutInflater().from(this), R.layout.weatherinfo, null, false);//使用databing的方法得到布局文件

                Weather weather = Utility.handleWeatherResponse(cityList.get(i).getWeather()); //解析json数组

                if (weather != null)
                {
                    showWeatherInfo(weatherinfoBinding, weather);
                }
                else
                {
                    requestWeather(cityList.get(i).getWeatherId());
                }
                viewList.add(weatherinfoBinding.getRoot()); //将此页添加到viewpager显示数组中

               //这里是解决swipeRefreshLayout与ScrollView冲突的地方,意思是只有当ScrollView滑到最顶端时才enable swipeRefreshLayout
                weatherinfoBinding.weatherScrollview.setOnTouchListener(new View.OnTouchListener()
                {
                    @Override
                    public boolean onTouch(View v, MotionEvent event)
                    {
                        binding.swipeRefresh.setEnabled(weatherinfoBinding.weatherScrollview.getScrollY() == 0);

                        return false;
                    }
                });

               //以下代码的功能是新建小圆点显示
                Button bt = new Button(this); 
                bt.setLayoutParams(new ViewGroup.LayoutParams(18, 18)); //设置长宽

         if (i == 0)
                {
                    bt.setBackgroundResource(R.drawable.home_page_dot_select);
                    mPreSelectedBt = bt;
                    binding.title.titleCtiy.setText(cityList.get(i).getCityName());
                }
                else
                {
                    bt.setBackgroundResource(R.drawable.icon_dot_normal);
                }

                binding.title.llContainer.addView(bt);

            }
        }
        else
        {
            binding.title.titleCtiy.setText(null);

        }

        binding.pager.setAdapter(new ViewPagerAdapter(viewList));
        binding.pager.setCurrentItem(0);
        binding.pager.setPageTransformer(true, new DepthPageTransformer()); //添加页面切换动画

    }
2、每小时天气的显示
由于api的原因,免费的api只能得到一天中每三个小时的天气显示,所以导致此处显示看起来很挫,付费api则可以得到一至三天的每小时天气信息,这样就好很多了。关于每小时天气的显示,其实就是一个横向的一个RecyclerView,代码中使用的是databing结合RecyclerView的方法实现,我也是本着减少findViewById的使用才使用databing来实现的。RecyclerView结合databing的方法可查看 http://www.jianshu.com/p/c27462a405fb
这里还有最重要的一点是databing结合glide实现图片加载,参考以下实现 http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0930/3536.html
具体代码在HourlyForecaseItemBean中。下面是代码中的具体实现。
@BindingAdapter({"imageUrl"})
public static void loadImage(ImageView view, String imageUrl)
{

    Glide.with(view.getContext()).load(imageUrl).into(view);

}
布局文件在hourlyforecaseitem中
<ImageView
    android:id="@+id/id_index_gallery_item_image"
    android:layout_width="40dp"
    android:layout_height="40dp"
    android:layout_gravity="center"
    app:imageUrl="@{item.imageUrl}"/>
3、城市管理界面的RecyclerView item长按删除
网上可以找到很多RecyclerView的长按的实现方法,我使用的方法是在首先实现 OnitemClickListener与OnItemLongClickListener接口然后在RecyclerViewAdapter的onBindViewHolder方法对长按短按进行绑定。
/*
* 设置item点击事件
* */
public void setOnItemClickListener(OnItemClickListener OnItemClickListener)
{
    this.mOnItemClickListener = OnItemClickListener;
}

public void setmOnItemLongClickListener(OnItemLongClickListener onItemLongClickListener)
{
    this.mOnItemLongClickListener = onItemLongClickListener;
}

/*
* 数据绑定
* */
@Override
public void onBindViewHolder(final BindingHolder holder, final int position)
{
    holder.bindData(items.get(position));


    if(mOnItemClickListener!=null)
    {
        holder.itemView.setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(View v)
            {
                int pos = holder.getLayoutPosition();
                mOnItemClickListener.onItemClick(pos);
            }
        });

    }

    if(mOnItemLongClickListener !=null)
    {
        holder.itemView.setOnLongClickListener(new View.OnLongClickListener()
        {
            @Override
            public boolean onLongClick(View view)
            {
                int pos = holder.getLayoutPosition();
                mOnItemLongClickListener.onItemClick(view,pos);
                return true ;
            }
        });

    }


}
实现了长按之后就可以来实现删除功能。根据之前的逻辑设计,删除操作其实是得到按下item的weatherId然后查询数据库进行删除,之后在重新加载界面完成的。
adapter.setmOnItemLongClickListener(new OnItemLongClickListener()
        {
            @Override
            public void onItemClick(View view, int position)
            {
//                Toast.makeText(CityManagementActivity.this, "onLongClick事件  您点击了第:" + position + "个Item" + "对应weatherid为" + cityList.get(position).getWeatherId(), LENGTH_SHORT).show();
                showPopMenu(view, position);
            }
        });

 public void showPopMenu(View view, final int position)
    {
        PopupMenu popupMenu = new PopupMenu(this, view);
        popupMenu.getMenuInflater().inflate(R.menu.delect_menu, popupMenu.getMenu());
        popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener()
        {
            public boolean onMenuItemClick(MenuItem item)
            {
                City mcity = cityList.get(position);//得到对应数组

                Utility.delectCityInfo(mcity.getWeatherId()); //查找数据库删除

                EventBus.getDefault().post(new MessageEvent("Refresh",null)); //通知mainactivity更新界面

                quayCities();
                return false;
            }
        });

     popupMenu.show();
    }



先写这么几个我觉得比较关键的地方吧 以后有机会在添加 具体还是查看代码吧 自觉代码写的比较烂 有bug请留言
  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值