android 练习之路 (六)
项目的github地址:https://github.com/Qunter/SearchAndCall
------------------------------------------------------------------------
今天做详情页以及列表的刷新,今天做完之后算暂时把最初版本的学校资讯给做完了
惯例,先上效果图
PS:那个𠇔的图片是因为我们学校有个校领导叫X𠇔X的名字,结果维护官网的同学可能是图简单就直接切了个图丢进去,命名又没命名好,我想了一两个小时都没想出来怎么筛选跳过他,没办法了,只能下次跟上面提意见看看能不能改改了
就从简单的说起吧,首先是列表刷新,这个刷新之前在新建页面的时候已经把SwipeRefreshLayout丢进去了,所以只要在Activity里初始化,写好逻辑代码就行了
那么需要增加的是一个标志,代表是否是第一次加载数据,因为每次加载数据的方法都写在getJsoupContent()里了,我们没必要再写一个和这个差不多的方法,直接加个标志进去,判断一下,最后得出结果然后返回一个不一样的msg就行了
private boolean ifFirstInitData=true;
那么还要多加个msg
private final int REFRESH=0x02;
以及对应的handler判断和adapter的刷新以及refresh标志的取消(不取消标志难道一直让他在那打转?笑)
case REFRESH: adapter.notifyDataSetChanged(); Toast.makeText(getContext(),"刷新成功",Toast.LENGTH_SHORT).show(); schoolSwipeRefreshLayout.setRefreshing(false); break;
然后在联网获取数据的方法getJsoupContent()加个if来判断标志位,决定到底发送哪条msg
if (ifFirstInitData){ ifFirstInitData=false; handler.sendEmptyMessage(INITRECYLERVIEW); }else{ handler.sendEmptyMessage(REFRESH); }
最后是全部代码
SchoolInfoFragmActivity.java
public class SchoolInfoFragmActivity extends Fragment { private List<SchoolInfo> schoolInfoList; private final int GETJSOUPCONTENT=0x00,INITRECYLERVIEW=0x01,REFRESH=0x02; private SwipeRefreshLayout schoolSwipeRefreshLayout; private SchoolInfoListAdapter adapter; private RecyclerView schoolRecyclerView; private boolean ifFirstInitData=true; private Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what){ case GETJSOUPCONTENT: new Thread(runnable).start(); break; case INITRECYLERVIEW: adapter = new SchoolInfoListAdapter(getContext(),schoolInfoList,schoolRecyclerView); adapter.setOnItemClickListener(new SchoolInfoListAdapter.OnItemClickListener() { @Override public void onItemClick(View view, int position) { //Log.e("onitemclickTag", schoolInfoList.get(position).getTitle()); Intent intent = new Intent(getContext(),SchoolDateilActivity.class); intent.putExtra("dateilUrl",schoolInfoList.get(position).getPageUrl()); startActivity(intent); } }); schoolRecyclerView.setAdapter(adapter); break; case REFRESH: adapter.notifyDataSetChanged(); Toast.makeText(getContext(),"刷新成功",Toast.LENGTH_SHORT).show(); schoolSwipeRefreshLayout.setRefreshing(false); break; } } }; public static SchoolInfoFragmActivity newInstance() { return new SchoolInfoFragmActivity(); } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { handler.sendEmptyMessage(GETJSOUPCONTENT); View view = inflater.inflate(R.layout.activity_school_info_fragm, container, false); schoolSwipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.swipe_refresh_school); schoolSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { @Override public void onRefresh() { handler.sendEmptyMessage(GETJSOUPCONTENT); } }); schoolRecyclerView = (RecyclerView) view.findViewById(R.id.recycler_view_school); LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity()); schoolRecyclerView.setLayoutManager(layoutManager); return view; } Runnable runnable = new Runnable() { @Override public void run() { getJsoupContent(); } }; /** * 使用jsoup获取学校官网数据 */ private void getJsoupContent(){ schoolInfoList = new ArrayList<>(); String url = "http://www.jxut.edu.cn/"; Connection conn = Jsoup.connect(url); // 修改http包中的header,伪装成浏览器进行抓取 conn.header("User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:32.0) Gecko/ 20100101 Firefox/32.0"); Document doc = null; try { doc = conn.get(); } catch (IOException e) { e.printStackTrace(); } Elements elements = doc.select("[class=tabContent blog]"); Elements elements1 = elements.select("font"); Elements elements2 = elements1.select("a"); Document dt = null; for(Element element : elements2){ String Title = element.text(); String PageUrl = element.attr("abs:href"); Connection ct = Jsoup.connect(PageUrl); ct.header("User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:32.0) Gecko/ 20100101 Firefox/32.0"); try { dt = ct.get(); } catch (IOException e) { e.printStackTrace(); } Elements elements3 = dt.select("[class=listpicabs_text_show]"); String ImgUrl = ""; if(elements3.select("img").first()==null){ }else{ ImgUrl = elements3.select("img").first().absUrl("src"); } schoolInfoList.add(new SchoolInfo(Title,PageUrl,ImgUrl)); Log.e("mytag", Title); Log.e("mytag", PageUrl ); Log.e("mytag", ImgUrl ); } if (ifFirstInitData){ ifFirstInitData=false; handler.sendEmptyMessage(INITRECYLERVIEW); }else{ handler.sendEmptyMessage(REFRESH); } } }
那么刷新就解决了
接下来完成一下这个详情页
这里说到两个开源框架,一个是banner,主要是做图片轮播的框架,另外一个就比较常用了,glide图片加载框架,嘛,这个的话之前就说了要用框架来加载图片,只不过是想再自己做一遍加载再复习一下过程而已,那么现在开始就用框架来实现这个功能了
首先是添加依赖,然后sync一下,这国际惯例了
//banner图片轮播依赖 compile 'com.youth.banner:banner:1.4.9' //glide图片加载依赖 compile "com.github.bumptech.glide:glide:3.7.0"
那么详情页,首先得有个页,有个layout
activity_dateil.xml
<?xml version="1.0" encoding="utf-8"?> <FrameLayout 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"> <android.support.design.widget.CoordinatorLayout android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true"> <android.support.design.widget.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:fitsSystemWindows="true"> <android.support.design.widget.CollapsingToolbarLayout android:id="@+id/coll_toolbar_layout" android:layout_width="match_parent" android:layout_height="@dimen/detail_image_height" android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" app:contentScrim="?attr/colorPrimary" app:expandedTitleMarginEnd="96dp" app:layout_scrollFlags="scroll|exitUntilCollapsed"> <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" app:layout_collapseMode="parallax" app:layout_collapseParallaxMultiplier="0.8" app:layout_scrollFlags="scroll|snap|enterAlways|enterAlwaysCollapsed"> <com.youth.banner.Banner xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/detail_image" android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="centerCrop" /> <View android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="bottom" android:background="@drawable/black_bg" /> <TextView android:id="@+id/detail_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom" android:layout_marginBottom="15dp" android:padding="@dimen/activity_vertical_margin" android:textColor="@android:color/white" android:textSize="22sp" /> <TextView android:id="@+id/detail_time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right|bottom" android:layout_marginBottom="5dp" android:padding="5dp" android:textColor="@android:color/white" android:textSize="16sp" /> </FrameLayout> </android.support.design.widget.CollapsingToolbarLayout> </android.support.design.widget.AppBarLayout> <android.support.v4.widget.NestedScrollView android:id="@+id/detail_scroll_view" android:layout_width="match_parent" android:layout_height="match_parent" android:overScrollMode="never" android:scrollbars="vertical" app:layout_behavior="@string/appbar_scrolling_view_behavior"> <TextView android:id="@+id/detail_content" android:layout_width="match_parent" android:layout_height="match_parent" android:isScrollContainer="false" android:overScrollMode="never" android:scrollbars="vertical" android:textSize="18sp"/> </android.support.v4.widget.NestedScrollView> </android.support.design.widget.CoordinatorLayout> </FrameLayout>
插句话,其实中间那个设置了背景的View可要可不要,之所以加只是为了在没有图片的时候有个过渡,直接空白的然后突然闪一张图片出来,感觉视觉效果不好吧
然后是做个glideImageLoder,当然了,banner的实现不一定要glide,但是必须得有个玩意来加载图片吧,那么得封装一下
GlideImageLoader.java
public class GlideImageLoader extends ImageLoader { @Override public void displayImage(Context context, Object path, ImageView imageView) { //具体方法内容自己去选择,次方法是为了减少banner过多的依赖第三方包,所以将这个权限开放给使用者去选择 Glide.with(context.getApplicationContext()) .load(path) .crossFade() .into(imageView); } }
然后就直接把这个详情页对应的Activity写完就行了
public class SchoolDateilActivity extends BaseActivity { private String dateilUrl,detailContent; private List<String> dateilImgUrl = new ArrayList<String>(); private Spanned contentsp; private TextView detailTitleTv,detailTimeTv,detailContentTv; private Banner banner; private String detailTitleString,detailTimeString; private final int GETDETAILDATA=0x00,PUTVIEWSDATA=0X01; private Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what){ case GETDETAILDATA: new Thread(deailDataRunnable).start(); break; case PUTVIEWSDATA: putViewsData(); initBanner(); detailTitleTv.setText(detailTitleString); detailTimeTv.setText(detailTimeString); break; } } }; @Override protected void initVariablesAndService() { //获取详情页链接 dateilUrl = getIntent().getExtras().get("dateilUrl").toString(); handler.sendEmptyMessage(GETDETAILDATA); } @Override protected void initViews(Bundle savedInstanceState) { setContentView(R.layout.activity_dateil); detailTitleTv = (TextView) findViewById(R.id.detail_title); detailTimeTv = (TextView) findViewById(R.id.detail_time); detailContentTv = (TextView) findViewById(R.id.detail_content); banner = (Banner) findViewById(R.id.detail_image); } Runnable deailDataRunnable = new Runnable() { @Override public void run() { getDetailData(); } }; /** * 获取详情页所需数据 */ private void getDetailData(){ Connection conn = Jsoup.connect(dateilUrl); // 修改http包中的header,伪装成浏览器进行抓取 conn.header("User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:32.0) Gecko/ 20100101 Firefox/32.0"); Document doc = null; try { doc = conn.get(); } catch (IOException e) { e.printStackTrace(); } detailTitleString = doc.select("[class=listpicabs_text_title]").text(); detailTimeString = doc.select("[class=listpicabs_text_info]").text(); detailTimeString = detailTimeString.substring(6,16); Elements detailImgUrlEles = doc.select("[class=listpicabs_text_show]").select("img"); Element element = doc.select("span").first(); doc.select("img").remove(); for(Element et:detailImgUrlEles){ dateilImgUrl.add(et.absUrl("src")); Log.e("detailimg", et.absUrl("src")); } Log.e("ttag", element.toString()); detailContent = element.toString(); while(detailContent.indexOf("<br> <br>")!=-1){ detailContent = detailContent.replace("<br> <br>","<br>"); } detailContent = detailContent.replace("。<br>","。<br> <br>"); if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) { contentsp = Html.fromHtml(detailContent,Html.FROM_HTML_MODE_LEGACY); } else { contentsp = Html.fromHtml(detailContent); } handler.sendEmptyMessage(PUTVIEWSDATA); } private void putViewsData(){ detailContentTv.setText(contentsp); } /** * 初始化Banner轮播 */ private void initBanner(){ //设置图片加载器 banner.setImageLoader(new GlideImageLoader()); //设置图片集合 banner.setImages(dateilImgUrl); //banner设置方法全部调用完毕时最后调用 banner.start(); } }
这里可以看到banner和glide的使用方法都非常简单,glide自不用说,很多开发者都在用,banner的话,从这个initBanner方法中也可以看出,最简单的使用只需要在页面里设置个banner控件,然后在Activity代码里初始化,然后设置对应的图片加载方法类,然后设置图片的url,再start就完事了
那么今天也就做到这,后面的应该进度会比较快,因为很多功能都是我之前在这个项目前身的寻呼都实现好了的,往里面加就是了,在这些都做完之后,我也还得优化一下,把能够重用的都封装一下