📜 ⬆️ ⬇️

Sample application using the library AQuery

We are constantly asked why we use the AQuery library in our projects. In the end, we were tired of responding and we decided to show what AQuery is capable of in combat.

But to write some strange pseudocode in the spirit of hello world is boring and uninteresting, and so we decided to make some small but useful application. Recently, the Megamind project was separated from Habr and in the comments to the news they expressed a proposal to combine the RSS feed from all resources. This is what we will do.

At the end, we get the following prototype of the IT News application (rss from Habr, Giktimes, Megamind and Siliconus / Swarm ordered by date):
')
image

Links for the rush:
github: github.com/recoilme/itnews
google play: play.google.com/store/apps/details?id=org.freemp.itnews

First a couple of words about the library itself.

The library is intended primarily for:
- manipulating UI elements
- work with the network
- image processing

This is just what is on the surface.

Tiny and without external dependencies. It does not impose its use, does not conflict with other libraries and does not impose any style when programming. You just throw a jar file and that's it.
So, in order:

Manipulating UI elements: write less, write faster
Code without AQuery
public void renderContent(Content content, View view) { ImageView tbView = (ImageView) view.findViewById(R.id.icon); if(tbView != null){ tbView.setImageBitmap(R.drawable.icon); tbView.setVisibility(View.VISIBLE); tbView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { someMethod(v); } }); } TextView nameView = (TextView) view.findViewById(R.id.name); if(nameView != null){ nameView.setText(content.getPname()); } TextView timeView = (TextView) view.findViewById(R.id.time); if(timeView != null){ long now = System.currentTimeMillis(); timeView.setText(FormatUtility.relativeTime(now, content.getCreate())); timeView.setVisibility(View.VISIBLE); } TextView descView = (TextView) view.findViewById(R.id.desc); if(descView != null){ descView.setText(content.getDesc()); descView.setVisibility(View.VISIBLE); } } 


AQuery code
 public void renderContent(Content content, View view) { AQuery aq = new AQuery(view); aq.id(R.id.icon).image(R.drawable.icon).visible().clicked(this, "someMethod"); aq.id(R.id.name).text(content.getPname()); aq.id(R.id.time).text(FormatUtility.relativeTime(System.currentTimeMillis(), content.getCreate())).visible(); aq.id(R.id.desc).text(content.getDesc()).visible(); } 


And no one forbids right there beside me to write findviewbyid - mix as you like. The code becomes more concise and easy to read as if writing not in Java, but on some Groovy or Kotlin.

Work with the network. Get, post, multipart requests. Dynamic binding with activation. Flexible out-of-box caching

Asyncapi
(I will continue to link to the wiki in order not to produce entropy)

Download images. Caching, animation, downsampling, aspect ratio manipulation - just forget about memory problems and get busy

ImageLoading

As well as authentication through a bunch of resources from facebook to tweeter. Work with locations. A bunch of debug utilities, XML parsing, and so on.

great documentation with lots of examples

But these are all words, let's try in action. Create an empty project with the following dependencies:


Let's start with the AQuery ad, check that everything is connected:

 public class ActivityMain extends Activity { private AQuery aq; private Activity activity; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); activity = this; aq = new AQuery(activity); AQUtility.setDebug(true); } } 


Now add the cards, and check how everything works:

 public class ActivityMain extends Activity { private AQuery aq; private Activity activity; private RecyclerView gridView; private StaggeredGridLayoutManager mLayoutManager; private AdapterMain adapter; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); activity = this; aq = new AQuery(activity); AQUtility.setDebug(true); gridView = new RecyclerView(activity); gridView.setHasFixedSize(true); mLayoutManager = new StaggeredGridLayoutManager(1,StaggeredGridLayoutManager.VERTICAL); gridView.setLayoutManager(mLayoutManager); gridView.setItemAnimator(new DefaultItemAnimator()); getWindow().setContentView(gridView); adapter = new AdapterMain(activity,new String[]{"123","456"}); gridView.setAdapter(adapter); } } 


Adapter

 /** * Created by recoilme on 23/01/15. */ public class AdapterMain extends RecyclerView.Adapter<AdapterMain.ViewHolder> { private String[] data; private AQuery aq; private Activity activity; public AdapterMain(Activity activity,String[] data) { this.activity = activity; this.data = data; aq = new AQuery(activity); } public static class ViewHolder extends RecyclerView.ViewHolder { public TextView mTextView; public ViewHolder(View v) { super(v); mTextView = (TextView) v.findViewById(R.id.articleTitle); } } @Override public AdapterMain.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View v = LayoutInflater.from(parent.getContext()) .inflate(R.layout.card, parent, false); ViewHolder vh = new ViewHolder(v); return vh; } @Override public void onBindViewHolder(ViewHolder viewHolder, int i) { aq.id(viewHolder.mTextView).text(data[i]); } @Override public int getItemCount() { return data.length; } } 


Layout cards:
 <?xml version="1.0" encoding="utf-8"?> <android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:card_view="http://schemas.android.com/apk/res-auto" android:id="@+id/card_view" android:layout_gravity="center" android:layout_width="match_parent" android:layout_height="wrap_content" card_view:contentPadding="8dp" card_view:cardBackgroundColor="@color/primary_bgr" card_view:cardUseCompatPadding="true" card_view:cardCornerRadius="4dp"> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/articleLayout" android:background="@color/primary_bgr" android:layout_width="match_parent" android:layout_height="wrap_content"> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:id="@+id/stgvImageView" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignLeft="@+id/stgvImageView" android:layout_alignRight="@+id/stgvImageView" android:gravity="center" android:layout_alignBottom="@+id/stgvImageView" android:layout_alignTop="@+id/stgvImageView" android:textColor="@color/white" android:textSize="20dp" android:id="@+id/siteurl" android:visibility="gone"/> <View android:layout_width="match_parent" android:layout_height="68dp" android:background="@drawable/main_adapter_tagbgr" android:layout_alignRight="@+id/stgvImageView" android:layout_alignTop="@+id/stgvImageView" android:layout_alignLeft="@+id/stgvImageView" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/stgvImageView" android:id="@+id/footer" android:orientation="vertical" android:paddingTop="16dp" android:paddingBottom="16dp" android:paddingLeft="6dp" > <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/articleTitle" android:textAppearance="@android:style/TextAppearance.Medium" android:textColor="@drawable/main_adapter_textselector" android:textStyle="bold" android:layout_marginBottom="16dp" android:paddingRight="8dp" android:paddingLeft="0dp"/> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" > <LinearLayout android:id="@+id/authorLayout" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" android:layout_alignParentLeft="true" android:layout_centerVertical="true" android:layout_alignParentStart="false" android:clickable="true" android:paddingTop="10dp" android:paddingBottom="10dp" android:baselineAligned="false" android:paddingRight="4dp" android:paddingLeft="0dp"> <ImageView android:id="@+id/userAva" android:layout_width="20dp" android:layout_height="20dp" android:layout_gravity="center_vertical" android:background="#f8f8f8" /> <TextView android:id="@+id/userFullname" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:textColor="@color/gray_text" android:paddingLeft="8dp" android:ellipsize="end" android:maxWidth="160dp" android:singleLine="true" android:textAppearance="@android:style/TextAppearance.Small"/> </LinearLayout> </RelativeLayout> </LinearLayout> </RelativeLayout> </android.support.v7.widget.CardView> 



Check what happened, should be something like this:


Now we begin magic with AQuery - we pull rss and parsim it:

just write aq.ajax (url, XmlDom.class, this, “onRequest”) - AQuery does the rest

 public void request(String url) { aq.ajax(url, XmlDom.class,this,"onRequest"); } public void onRequest(String url,XmlDom xml, AjaxStatus status) { if (status.getCode()==200) { String logo = ""; try { logo = xml.tags("url").get(0).text(); } catch (Exception e) { e.printStackTrace(); } List<XmlDom> xmlItems = xml.tags("item"); for(XmlDom xmlItem: xmlItems){ ClassItem item = new ClassItem(); String description = xmlItem.tag("description").text(); item.setLogo(logo); item.setAuthor(xmlItem.tag("author").text()); item.setTitle(xmlItem.tag("title").text()); item.setDescription(description); item.setLink(xmlItem.tag("link").text()); String pubDate = xmlItem.tag("pubDate").text(); Date date = new Date(); try { date = formatter.parse(pubDate); } catch (Exception e) { AQUtility.debug("errorParsingDate",e.toString()); } item.setDate(date); String src = ""; try { src = new XmlDom("<xml>"+description+"</xml>").tag("img").attr("src"); if (src.startsWith("//") ) { src = "http:"+src; } } catch (Exception e) { e.printStackTrace(); } item.setImg(src); items.add(item); } adapter.notifyDataSetChanged(); } } 

The AjaxStatus class comes with detailed information about the results of executing the query + and a simple XML parser is built into AQuery. There is no need to worry about the availability of activation at the time of the request, AQuery will do it for us. Plus the HTTP request module is much more flexible than in the example above, you can customize everything, from the headers to the request execution method. And if you need, for example, to cache a request, simply add the fileCache = true parameter and the time for which the request should be cached. There is a functional for invalidating the cache in case of an error, for example, and so on.

We will now return to the adapter, and enrich the rss stream with the functionality of displaying pictures. Moreover, with AQuery it's not easy, but very simple:
 public class AdapterMain extends RecyclerView.Adapter<AdapterMain.ViewHolder> { private ArrayList<ClassItem> data; private AQuery aq; private Activity activity; private DateFormat formatter = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm", Locale.getDefault()); public AdapterMain(Activity activity,ArrayList<ClassItem> data) { this.activity = activity; this.data = data; aq = new AQuery(activity); } public static class ViewHolder extends RecyclerView.ViewHolder { private ImageView stgvImageView; private ImageView userAva; private TextView siteurl; private TextView userFullname; private TextView articleTitle; public ViewHolder(View holderView) { super(holderView); stgvImageView = (ImageView) holderView.findViewById(R.id.stgvImageView); siteurl = (TextView) holderView.findViewById(R.id.siteurl); userAva = (ImageView) holderView.findViewById(R.id.userAva); userFullname = (TextView) holderView.findViewById(R.id.userFullname); articleTitle = (TextView) holderView.findViewById(R.id.articleTitle); } } @Override public AdapterMain.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View v = LayoutInflater.from(parent.getContext()) .inflate(R.layout.card, parent, false); ViewHolder vh = new ViewHolder(v); return vh; } @Override public void onBindViewHolder(ViewHolder viewHolder, int i) { ClassItem item = data.get(i); aq.id(viewHolder.articleTitle).text(item.getTitle()); aq.id(viewHolder.siteurl).text(item.getLink()); aq.id(viewHolder.userAva).image(item.getLogo()); aq.id(viewHolder.userFullname).text(item.getAuthor() + " " + formatter.format(item.getDate())); if (TextUtils.equals(item.getImg(),"")) aq.id(viewHolder.stgvImageView).gone(); else { aq.id(viewHolder.stgvImageView).visible().image(item.getImg(), true, false, 640, 0, null, AQuery.FADE_IN, AQuery.RATIO_PRESERVE); } } @Override public int getItemCount() { return data.size(); } } 


Literally in one line, we otunuskailili image, included caching it in memory and otkseylili to the desired aspect ratio, simultaneously turning on the display animation.

  aq.id(viewHolder.stgvImageView).visible().image(item.getImg(), true, false, 640, 0, null, AQuery.FADE_IN, AQuery.RATIO_PRESERVE); 


If you look at the library code, you can see that all the logic of working with images is based on weakReference, the LRU cache is applied, scaling is performed using optimized inSampleSize methods, and so on. Moreover, it is possible to manually control caching parameters from cache sizes for various types of pictures (small, large, medium) up to the cache method and the number of pictures that are simultaneously stored in the cache.

An example of a config with file system caching disabled (application fragment)

 @Override public void onLowMemory(){ //clear all memory cached images when system is in low memory //note that you can configure the max image cache count, see CONFIGURATION BitmapAjaxCallback.clearCache(); } @Override public void onCreate() { //Config cache BitmapAjaxCallback.setDelayWrite(true); BitmapAjaxCallback.setPixelLimit(640*800); BitmapAjaxCallback.setMaxPixelLimit(4096000); } 


In this case, the pictures will not be downloaded to a temporary file, which can be convenient when you download hundreds of images at a time.

And this is how forced cache flush looks like, for example, when the page is reloaded:

 BitmapAjaxCallback.clearCache(); 


And in our example, in fact, it remains to add a page update:

  @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ... swipeRefreshLayout = new SwipeRefreshLayout(activity); swipeRefreshLayout.setOnRefreshListener(this); swipeRefreshLayout.setColorScheme(android.R.color.holo_blue_bright, android.R.color.holo_green_light, android.R.color.holo_orange_light, android.R.color.holo_red_light); .... @Override public void onRefresh() { getFeeds(); } 


Well, expand the list of feeds:

  private final String[] FEEDS = new String[]{"http://roem.ru/rss/","http://siliconrus.com/feed/","http://habrahabr.ru/rss/","http://megamozg.ru/rss/","http://geektimes.ru/rss/"}; 


The result was an application for reading rss tapes from the pool of its basic resources, which thanks to AQuery was able to write in just 5 hours, without dancing with connecting a heap of libraries, focusing on the code itself, and not on the process. What we love AQuery &)

A spoon of tar - the library is rarely updated and practically does not develop. What, on the one hand, says about its maturity, and on the other, that the developer wanted to raise money for its development before the framework, but was disappointed in the donation model and scored. But over the years of its use, I haven’t been lucky enough to encounter a single error in its code, which I sincerely wish other libraries.

PS: I posted the resulting rss reader on github. Of course, this is only a prototype, but quite working:
github.com/recoilme/itnews
google play: play.google.com/store/apps/details?id=org.freemp.itnews

Source: https://habr.com/ru/post/248647/


All Articles