Content
Last summer (already), I had a great desire to write a client for Android-web service Vimeo . I like this service, and as for me, it would be convenient to watch for updates in subscriptions to the video device.
I conceived this project for myself as an academic (in the sense that I'm learning), but the result was to make quite a tangible part (you can see screenshots of what is done ), but it has not yet been completed. This client does not just me, his version is almost the same time (he was the first) began to make me makotosan and his version so far, it seems, also has done).
In any case, the process of writing project, I gained some knowledge base, which I hasten to share. Not all threads exclusive, but some see the subtleties are not disclosed on the Internet or buried pretty deep in its bowels. I will give examples of additional codes iskhodnyh vimeoid, this will allow you podcmotret as the topic under consideration operates in real time (NB: some links lead to specific line in the code).
Aside from the usual use of
(Hereafter I will use the word as an analogue to the English point item, to distinguish between a list item, which may be heading, and the point from point customary element, which can not be a title)
Headers should not react to clicks and the selection and must have their own kind. This can be achieved by redefining except
Then constant containing the number of types of elements (in our case - two):
The adapter will contain information on all elements, so the implementation
Method
In my case I keep the adapter list of sections that contain them owned items. Thus, each partition can be asked how many items within it at the cost of this to know the type.
This method can now be used to override the
The method
Method
By the way, you can store a reference to
That's all you need to work with a list of sections, if necessary - look over here, but first give some explanations.
The example I use a structure for storing data of the sections and paragraphs. The structure of the partition is stored identifier section, its title and structure of the items contained therein. Structure keeps a pointer to point to the parent structure of the section heading, paragraph, the path to the icon and the click handler on the point (about it in the next chapter). The designers of both structures are available only for adapters:
Method
Sometimes you need to when you click on the item list, it changed its status and / or switched aktiviti. For example, the "Follow" in the list of actions on account of Twitter may contain an icon with a minus, if you have not follovili this person and change the icon on the plus and come after you for confirmation of follovinge. You can handle the selected item in the current
And using it aktiviti you can do:
In my case, for points in each section serve some kind of action - go to aktiviti or change the view point of the request to the server. So I chose to make the structure of the publicly available properties for sections and paragraphs, and the structure of the items contain handler
Using the above method
If at some point need to upgrade (invalidirovat) The specific form of the element well-known (or even a child form) of the list at a time when it is visible on the screen, you can call
If you create a list of
My version from the same source, this solution Fedor Vlasov , corrected for my needs. First, I made a directory for storing cached images of static - that is, once it is created during the lifetime of the application and consistently clears the call
This chapter deals with the pagination in the
If you write a client for any complex web service - you are confronted with the problem of authorization, the vast number of web services for its implementation now uses OAuth and Vimeo just among those.
It is not necessary to write the implementation itself is somewhat thankless task, since there is already a great library signpost and better alternatives as far as I know, yet.
The algorithm first authorization to Android is the following:
Important note: on Android signpost works only with
Still, the behavior
Therefore, if in your case, you have enough call
If you also had to get the flow manually, I will turn your attention to a couple of points, the rest just show you the code, it should tell it all himself:
If the boot stream is thrown, the message it has shown through
Running
The code of the stream on the URL something like this:
9. Queues of
If you often have to perform several background tasks one by one (when completed one - to run the next), this free pattern that hides itself in the transitions to the linked list, you will do. For example, you might want to run at startup Activity consecutive multiple requests to the API of a Web service or database. The main thing is that the parameter types and result of all these problems have always been the same.
Here is the interface problem, which knows that it has the following problem:
An interface that monitors all the moments when the task successfully or unsuccessfully performed:
The interface
And most importantly, the implementation of the queue:
Now in your aktiviti at any time you can easily create all background tasks:
By the way, thanks to the interface
In the picture shows a blue band, this custom highlight the selected item, it has four states - pressed, which has a focus that is prohibited by and pressed the animation of the transition from a jammed for a long tapa. The first three and squeezed state - the so-called
In order to describe the state to highlight the selection in the layout, specify
From 9-patch is a cunning, too, just what is wrong in the layout - and they drive around and goes around the entire list, too. The main rule is - check the above description of the
QuickActions - a small library for pop-up dialogs with activities such as in the figure (and not only these, because their design can be changed easily.) They have become a new popular trend of the appearance of an official twitter client. Must be other implementation, in vimeoid I use this, and it also touched up a bit for their needs.
In order to display a dialog instead of the context menu when you long to Tape element on the list, enough to override a method
If your application aktiviti many different and they are invoked in a similar way, it might be convenient to transfer their calls including filling
Maybe this is obvious, but the rows of
Upd. It turned out as I thought it was a bicycle: There is a standard function
Be sure to read these articles in infleyter android really very sensitive to the complex structures and if you write a complex application, leyauty sooner or later will have to optimize:My pererenderivayuschiesya leyauty often at one point and failed
- Entry
- About the adapters list (
ListView
) with subcategories (for grouping items in the list) - About the lists that contain any of the (list items do anything complicated or alter themselves for selection)
- About forced invalidation of the lists
- About caching images for
ListView
(for lists with images) - About adapters, iterates through the cursor (to support pagination on the list)
- About authorization via OAuth to Android
- About using
MediaPlayer
and bufferring video feed over HTTP - About the turn of a few
AsyncTask
(alternate to perform background tasks) - About changing illumination of the selected item in
ListView
- About adding QuickActions in project
- Three more small solving
1. Entry
Last summer (already), I had a great desire to write a client for Android-web service Vimeo . I like this service, and as for me, it would be convenient to watch for updates in subscriptions to the video device.
I conceived this project for myself as an academic (in the sense that I'm learning), but the result was to make quite a tangible part (you can see screenshots of what is done ), but it has not yet been completed. This client does not just me, his version is almost the same time (he was the first) began to make me makotosan and his version so far, it seems, also has done).
In any case, the process of writing project, I gained some knowledge base, which I hasten to share. Not all threads exclusive, but some see the subtleties are not disclosed on the Internet or buried pretty deep in its bowels. I will give examples of additional codes iskhodnyh vimeoid, this will allow you podcmotret as the topic under consideration operates in real time (NB: some links lead to specific line in the code).
2. Lists with subcategories
Aside from the usual use of
ListView
, is often required to make a list in which items are grouped into several sections in the form header section, paragraph, item, item, ..., a section heading, paragraph, item, ..., a section heading, paragraph, point, and so on ... In the picture are sections «Statistics» and «Information».(Hereafter I will use the word as an analogue to the English point item, to distinguish between a list item, which may be heading, and the point from point customary element, which can not be a title)
Headers should not react to clicks and the selection and must have their own kind. This can be achieved by redefining except
getView
method getItemViewType
, getViewTypeCount
and isEnabled
adapter of this list and it otnasledovav, for example, BaseAdapter
.public class SectionedItemsAdapter extends BaseAdapter {. . .
- An example of vimeoid:
SectionedActionsAdapter
enum
to a set of identifiers):public static final int ITEM_VIEW_TYPE = 0; \ \ item public static final int SECTION_VIEW_TYPE = 1 \ \ section
Then constant containing the number of types of elements (in our case - two):
public static final int VIEW_TYPES_COUNT = SECTION_VIEW_TYPE + 1;
The adapter will contain information on all elements, so the implementation
getCount
, getItem
and getItemId
depend on your situation.Method
getItemViewType
should return the type of the constant element in his position. For an undetermined type of element in the class Adapter
there is a constant IGNORE_ITEM_VIEW_TYPE
.public int getItemViewType (int position) { if (...) return ITEM_VIEW_TYPE; if (...) return SECTION_VIEW_TYPE; return IGNORE_ITEM_VIEW_TYPE; }
In my case I keep the adapter list of sections that contain them owned items. Thus, each partition can be asked how many items within it at the cost of this to know the type.
This method can now be used to override the
getView
:public View getView (int position, View convertView, ViewGroup parent) { final int viewType = getItemViewType (position); if (viewType == IGNORE_ITEM_VIEW_TYPE) throw new IllegalStateException ("Failed to get object at position" + position); if (viewType == SECTION_VIEW_TYPE) { convertView =. . . / / Here you can get through LayoutInflater Layout for the section title } Else if (viewType == ITEM_VIEW_TYPE) { convertView =. . . / / Here you can get through LayoutInflater Layout for the item } return convertView; }
The method
isEnabled
should return false
for the items that can not be pressed and that can not move the cursor, and true
for others. Here, again, will getItemViewType
:public boolean isEnabled (int position) { return getItemViewType (position)! = SECTION_VIEW_TYPE};
Method
getViewTypeCount
returns the most constant, the number of possible types of elements: public int getViewTypeCount () {return VIEW_TYPES_COUNT;}
By the way, you can store a reference to
LayoutInflater
in the adapter and get it from that created it aktiviti through the constructor.That's all you need to work with a list of sections, if necessary - look over here, but first give some explanations.
The example I use a structure for storing data of the sections and paragraphs. The structure of the partition is stored identifier section, its title and structure of the items contained therein. Structure keeps a pointer to point to the parent structure of the section heading, paragraph, the path to the icon and the click handler on the point (about it in the next chapter). The designers of both structures are available only for adapters:
- An example of vimeoid:
LActionItem
public int addSection (String title); public LActionItem addItem (int section, int icon, String title);
Method
addSection
returns a group ID, which can then be used to add items to this group:final int suitsSection = adapter.addSection ("Suits"); adapter.addItem (suitsSection, R.drawable.heart, "Hearts"); adapter.addItem (suitsSection, R.drawable.diamond, "Diamonds"); adapter.addItem (suitsSection, R.drawable.spade, "Spades"); adapter.addItem (suitsSection, R.drawable.cross, "Crosses"); final int figuresSection = adapter.addSection ("Figures"); adapter.addItem (figuresSection, R.drawable.king, "King"); adapter.addItem (figuresSection, R.drawable.queen, "Queen"); . . .
3. Lists with responsive elements
Sometimes you need to when you click on the item list, it changed its status and / or switched aktiviti. For example, the "Follow" in the list of actions on account of Twitter may contain an icon with a minus, if you have not follovili this person and change the icon on the plus and come after you for confirmation of follovinge. You can handle the selected item in the current
ListActivity
and depending on the position to make a decision, but if the list is contained somewhere within the normal Activity
, then perhaps the choice is easier to handle in the adapter.- An example of vimeoid:
SectionedActionsAdapter
- Uses:
LActionItem
- Used in:
SingleItemActivity_
OnItemClickListener
:public class ActionsAdapter extends. . . implements OnItemClickListener
And using it aktiviti you can do:
final ListView actionsList = (ListView) findViewById (R.id.actionsList); final SectionedActionsAdapter actionsAdapter = new ActionsAdapter (...); . . . / / Fill the adapter values actionsList.setAdapter (actionsAdapter); actionsList.setOnItemClickListener (actionsAdapter);
In my case, for points in each section serve some kind of action - go to aktiviti or change the view point of the request to the server. So I chose to make the structure of the publicly available properties for sections and paragraphs, and the structure of the items contain handler
OnClick
which takes View
on which there was a choice, so you can change the View
directly from them. Thanks to this adapter, you can simply pass the handler:public void onItemClick (AdapterView <?> parent, View view, int position, long id) { final LActionItem item = (LActionItem) getItem (position); if (item.onClick! = null) item.onClick (view); }
Using the above method
addItem
can install the handler:final LActionItem heartsItem = adapter.addItem (suitsSection, R.drawable.heart, "Hearts"); heartsItem.onClick = new OnClickListener () {public void onClick (View view) {. . . }};
4. Forced invalidation of the lists
ListView
in Android, as we know, are arranged with the trick, this trick - ListView Recycler . Prinitsip Recycler'and, if briefly, is that if the list of items than fit on the screen as you scroll down the list of kinds of new elements are created and reused the old forms - in this implementation work prinitsipe getView
in adapters.If at some point need to upgrade (invalidirovat) The specific form of the element well-known (or even a child form) of the list at a time when it is visible on the screen, you can call
ListView.invalidate()
orAdapter.notifyDataSetChanged()
, but sometimes these irrational methods update and neighboring species, if at all, all visible (especially if the layout is built correctly ). Is there a way to get the current form of a list item using the method ListView.getChildAt(position)
. However, the position
in this case is not an index entry in the list, as would be expected, and the index relative to visible on-screen forms. So useful are these methods:public static View getItemViewIfVisible (AdapterView <?> holder, int itemPos) { int firstPosition = holder.getFirstVisiblePosition (); int wantedChild = itemPos - firstPosition; if (wantedChild <0 | | wantedChild> = holder.getChildCount ()) return null; return holder.getChildAt (wantedChild); } public static void invalidateByPos (AdapterView <?> parent, int position) { final View itemView = getItemViewIfVisible (parent, position); if (itemView! = null) itemView.invalidate (); }
invalidateByPos
update form only if it is visible on the screen (by force causing getView
adapter), and if the item does not see - getView
adapter will be automatically called when the form appears in the sight when you scroll through the list. To update a child form of the element, you can use the getViewIsVisible
, it will return the form element from which you can access its child types and null
, if the view is not visible to the user and updating is not necessary.- Methods described in class:
Utils
5. About caching images for lists
If you create a list of
ListView
, which contains an image downloaded from the network, this chapter is for you.It is unwise to be on every call getView
adapter to get the image on the URL again - of course it would be better to a) cache b) request only if the form with the image visible to the user. At the moment this problem is so often the programmers would get up on Android, there is already a lot of her decisions .My version from the same source, this solution Fedor Vlasov , corrected for my needs. First, I made a directory for storing cached images of static - that is, once it is created during the lifetime of the application and consistently clears the call
clearCache
(this method is useful to call in onDestroy()
in aktiviti using ImageLoader
orfinalize()
for using it adapter), slightly changed the way the creation of this catalog (seeUtils.createCacheDir()
). Second, the designer can pass ids of images to be displayed on site Pictures in the process of downloading and / or download if it failed. In the third a couple of small changes. In general, this class could be done and Singleton, changing settings before using it, but it's up to you. In my case, one by one instance is created for each running ListActivity
and passed to each adapter in need ListView
(or created in the adapter itself, if the ListView
is inside the regular Activity
). The basic method - displayImage(String url, ImageView view)
, the definition speaks for itself.- Source of vimeoid:
ImageLoader
- Uses the methods of:
Utils
6. Adapters, iterates through the cursors
This chapter deals with the pagination in the
ListView
. That is, the user sees the first n
items, scrolls the list up to n
-matrix element, and only after a request is made for the next n
elements of the database or the server.Then, the user scrolls down the list until the element 2n
, and we request the following pack sizes n
, etc. Invimeoid I make the following query when you click on footerView
that says "Load more ..." from the list: do not automatically, but the technique is roughly the same. </ p- Download by clicking on
footerView
:ItemsListActivity_
- The implementation for the guest:
ItemsListActivity
- Implementation for registered users:
ItemsListActivity
AsyncTask
, after which the background Vimeo API call informs the caller aktiviti, remained there still elements and not the last page whether it is, and aktiviti updates its forms in accordance with these data.- The adapter contains a set of cursors:
EasyCursorsAdapter
getView()
, if requested by one of the last items, run a query on the following page (preferably - AsyncTask
), which in receiving a new container will add it to the adapter and the adapter can cause notifyDataSetChanged()
.Like this:private final Page [] pages = new Page [MAX_PAGES_COUNT]; public View getView (final int position, View convertView, ViewGroup parent) { if (! waitingNextPage & & (Pages.length <MAX_PAGES_COUNT) & & (Position> = ((pages.length * PER_PAGE) - 2))) { final AsyncTask <Integer,. . .> NextPageTask =. . .; nextPageTask.execute (pages.length); / / NextPageTask is addSource, when it receives a new page waitingNextPage = true; } . . . } public void addSource (Page page) { if (pages.length> = MAX_PAGES_COUNT) return; pages [pages.length] = page; waitingNextPage = false; notifyDataSetChanged (); }
EasyCursorsAdapter
- a good example, where as a counterpart to Page
appears Cursor
. Surely there are alternatives would be happy if they mentioned in the comments.7. Sign in via OAuth to Android
If you write a client for any complex web service - you are confronted with the problem of authorization, the vast number of web services for its implementation now uses OAuth and Vimeo just among those.
It is not necessary to write the implementation itself is somewhat thankless task, since there is already a great library signpost and better alternatives as far as I know, yet.
- An example of vimeoid:
VimeoApi
- Uses a signpost:
JsonOverHttp
- Aktiviti that receives user token:
ReceiveCredentials
- His description on the manifest:
AndroidManifest.xml
vimeoid://oauth.done
) (but in the case of Android it pass when asked to /request_token
). Usually this is done via the web interface of the service.The algorithm first authorization to Android is the following:
- Specify the signpost where the service entry points are located in OAuth
- Request to
/request_token
get a couple of token / secret annex for unauthorized requests for this key (kolbek-URLvimeoid://oauth.done
pass here):provider.retrieveRequestToken(Uri callbackUri)
. NB:retrieveRequestToken
not return a token, and immediatelyUri
, sameauthUri
by which we must turn to next paragraph - Aktiviti run a browser, go to
/authorize
, by passing a token application and, if necessary, adding additional parameters to the required rights:startActivity(new Intent(Intent.ACTION_VIEW, authUri + ...))
- The user will see the page in the style of "Allow this application to access your account?" (When he logged out of service, he will offer to login). If it allows access, the browser is redirected to the address kolbeka
vimeoid://oauth.done?...
, but since yourAndroidManifest.xml
to capture such a special URL described aktiviti, Android user returns to your application, it is the opening aktiviti -ReceiveCredentials
- In aktiviti
ReceiveCredentials
you get the user token in the parameters ofUri uri = getIntent().getData()
, now on the token to obtain the secret through a request to/access_token
:provider.retrieveAccessToken(Uri uri)
- Now you can save the token and secret user, for example, in private
SharedPreferences
:consumer.getToken()
,consumer.getTokenSecret()
consumer.sign(Object request)
. If your application has been restarted, before any requests can be checked if there are any tokens SharedPreferences
, if there is - to remind them signpost
'in: consumer.setTokenWithSecret(String token, String secret)
, and if not - ask the secret user again (or refresh tokens, if the Web service allows it).Important note: on Android signpost works only with
CommonsHttpOAuthConsumer
/ CommonsHttpOAuthProvider
.Classes DefaultOAuth*
do not work.8. Media Player buffering and video via HTTP
MediaPlayer
as it turned out, it is very difficult to get to work just as you want, when playing videos. To get the video I had to perform an unusual request from the HTTP-specific headers, so getting the stream and had to write by hand bufferizirovanie. Streaming on analogue samples for audio files , I did not work, so for now I just load the video completely and start playing when it is already loaded (if your card is not enough space, I warn the user). When you close the player or playing I failed to clear the cache.Still, the behavior
VideoView
/ SurfaceView
switching within a single species leyauta also works very mixed (black screen every other time), so we had to leave in the layout banal single VideoView
and show ProgressDialog
over him while the video loads. Again, if you know something about the streaming video means MediaPlayer
(or the receipt of the chunks by hand), write in the comments.Therefore, if in your case, you have enough call
MediaPlayer.setDataSource(Uri uri)
, you can skip the next paragraph for more in her and does not tell.If you also had to get the flow manually, I will turn your attention to a couple of points, the rest just show you the code, it should tell it all himself:
- An example of vimeoid:
VimeoVideoPlayingTask
- Called from aktiviti:
Player
- Leyaut:
player.xml
AsyncTask
. I just aggregate MediaPlayer
inside ...PlayingTask
for convenience, you can choose any other way, but it's definitely better to get flow through AsyncTask
. In this case, the methodonPreExecute
you can prepare your player and customize it to doInBackground
get the video stream and return the stream to onPostExecute
, where and start playing. Again, it is convenient to display the download progress percentage, because doInBackground
known quantity of the data.If the boot stream is thrown, the message it has shown through
runOnUiThread
, because the task was interrupted.Running
getWindow().setFormat(PixelFormat.TRANSPARENT);
intended to displayed over player types do not go over it after hiding it. Although if you want to use ViewSwitcher
, it still does not help.The code of the stream on the URL something like this:
public static InputStream getVideoStream (long videoId) throws FailedToGetVideoStreamException, VideoLinkRequestException { try { final HttpClient client = new DefaultHttpClient (); . . . HttpResponse response = client.execute (request); if ((response == null) | | (response.getEntity () == null)) throw new FailedToGetVideoStreamException ("Failed to get video stream"); lastContentLength = response.getEntity (). getContentLength (); return response.getEntity (). getContent (); } Catch (URISyntaxException use) { throw new VideoLinkRequestException ("URI creation failed:" + use.getLocalizedMessage ()); } Catch (ClientProtocolException cpe) { throw new VideoLinkRequestException ("Client call failed:" + cpe.getLocalizedMessage ()); } Catch (IOException ioe) { throw new VideoLinkRequestException ("Connection failed:" + ioe.getLocalizedMessage ()); } }
9. Queues of AsyncTask
If you often have to perform several background tasks one by one (when completed one - to run the next), this free pattern that hides itself in the transitions to the linked list, you will do. For example, you might want to run at startup Activity consecutive multiple requests to the API of a Web service or database. The main thing is that the parameter types and result of all these problems have always been the same.
Here is the interface problem, which knows that it has the following problem:
public interface HasNextTask <Params> { public int getId (); void setNextTask (HasNextTask <Params> task); public HasNextTask <Parames> getNextTask (); public AsyncTask <?,?,?> execute (Params. .. params); / / Overlap with AsyncTask <Params, ...> }
An interface that monitors all the moments when the task successfully or unsuccessfully performed:
public interface PerformHandler <Params, Result> { public void onPerfomed (int taskId, Result result, HasNextTask <Params> nextTask); public void onError (Exception e, String description); }
The interface
HasNextTask
. What is represented by dots, can be taken in the child class or the class itself to make the abstract, that the methods doInBackground
/ onPostExecute
implemented directly in createTask
queue:public class TaskInQueue <Params, Result> extends AsyncTask <Params, Void, Result> implements HasNextTask <Params> { private final int taskId; private HasNextTask <Params> nextTask = null; private final PerformHandler <Params, Result> listener; public TaskInQueue (PerformHandler <Params, Result> listener, int taskId) { this.taskId = taskId; this.listener = listener; } @ Override public Result doInBackground (Params. .. params) {. . . / * Task * /} @ Override protected void onPostExecute (Result result) { . . . / / Handle the result if necessary listener.onPerformed (taskId, result, nextTask); } @ Override public int getId () {return taskId;} @ Override public void setNextTask (HasNextTask <Params> nextTask) { if (this.nextTask! = null) throw new IllegalStateException ("Next task is already set"); this.nextTask = nextTask; } @ Override public HasNextTask <Params> getNextTask () {return nextTask;}; }
And most importantly, the implementation of the queue:
public abstract class TasksQueue <Params, Result> implements PerformHandler <Params, Result>, Runnable { public static final String TAG = "TasksQueue"; private HasNextTask <Params> firstTask = null; private HasNextTask <Params> lastTask = null; private Map <Integer, Params> tasksParams = null; private int currentTask = -1; private boolean running = false; / / now runs one of the tasks private boolean started = false; / / turn launched private int size = 0; protected HasNextTask <Params> createTask (int taskId) {/ / you can override return new TaskInQueue <Params, Result> (this, taskId); } @ Override public HasNextTask <Params> add (int taskId, Params params) { Log.d (TAG, "Adding task" + taskId); final HasNextTask <Params> = createTask (taskId); if (isEmpty ()) { firstTask = task; lastTask = task; tasksParams = new HashMap <Integer, Params> (); } Else { lastTask.setNextTask (task); lastTask = task; } tasksParams.put (task.getId (), params); size + = 1; return task; } @ Override public void run () { Log.d (TAG, "Running first task"); if (! isEmpty ()) try { started = true; execute (firstTask); } Catch (Exception e) { onError (e, e.getLocalizedMessage ()); finish (); } else throw new IllegalStateException ("Queue is empty"); } @ Override public void onPerfomed (int taskId, Result result, HasNextTask <Params> nextTask) { Log.d (TAG, "Task" + taskId + "performed"); if (taskId! = currentTask) throw new IllegalStateException ("Tasks queue desynchronized"); running = false; try { if (nextTask! = null) { execute (nextTask); } Else finish (); } Catch (Exception e) { onError (e, "Error while executing task" + ((NextTask! = Null)? NextTask.getId (): taskId)); finish (); } } protected void execute (HasNextTask <Result> task) throws Exception { Log.d (TAG, "Trying to run task" + task.getId ()); if (running) throw new IllegalStateException ("Tasks queue desynchronized"); currentTask = task.getId (); running = true; Log.d (TAG, "Running task" + task.getId ()); task.execute (tasksParams.get (task.getId ())). get (); / / wait for result } protected void finish () { firstTask = null; lastTask = null; if (tasksParams! = null) tasksParams.clear (); tasksParams = null; currentTask = -1; running = false; started = false; size = 0; } public boolean isEmpty () {return (firstTask == null);} public boolean started () {return started;} public boolean running () {return running;} public int size () {return size;} }
Now in your aktiviti at any time you can easily create all background tasks:
protected final TasksQueue secondaryTasks; private final int TASK_1 = 0; private final int TASK_2 = 1; private final int TASK_3 = 2; public ... Activity () {/ / constructor secondaryTasks = new TasksQueue <..., ...>() { / / Here you can override createTask @ Override public void onPerfomed (int taskId, ... result) throws JSONException { super.onPerfomed (taskId, result); onSecondaryTaskPerfomed (taskId, result); } @ Override public void onError (Exception e, String message) { Log.e (TAG, message + "/" + e.getLocalizedMessage ()); Dialogs.makeExceptionToast (ItemsListActivity.this, message, e); } }; secondaryTasks.add (TASK_1, ...); secondaryTasks.add (TASK_2, ...); secondaryTasks.add (TASK_3, ...); } protected void someMethod () { . . . if (! secondaryTasks.isEmpty ()) secondaryTasks.run (); . . . } protected void onSecondaryTaskPerfomed (int taskId, ... result) { switch (taskId) { case TASK_1:. . . case TASK_2:. . . case TASK_3:. . . . . . } }
By the way, thanks to the interface
Runnable
such a queue can be run in a separate thread: new Thread (secondaryTasks, "Tasks Queue"). start ();
- In turn vimeoid:
ApiTasksQueue
- Created in:
SingleItemActivity
- Initialized in the task:
UserActivity
- Processing of completed tasks in:
UserActivity
10. Backlight selection in ListView
In the picture shows a blue band, this custom highlight the selected item, it has four states - pressed, which has a focus that is prohibited by and pressed the animation of the transition from a jammed for a long tapa. The first three and squeezed state - the so-called
9-patch
, you've probably heard about them , the animation - xml
-file animations.In order to describe the state to highlight the selection in the layout, specify
android:listSelector="@drawable/selector_bg"
for your ListView
.selector_bg.xml
- this is another xml
-file, a set of rules about how the backlit, depending on conditions. The system goes through each rule and as soon as the first rule is matched, it is true, but these are ignored. The algorithm is simple, but the build rules in the right order do not always work out right away. See examples:- Description:
selector_bg.xml
- Animation:
selector_bg_transition.xml
- Declared in:
generic_list.xml
From 9-patch is a cunning, too, just what is wrong in the layout - and they drive around and goes around the entire list, too. The main rule is - check the above description of the
ListView
, make sure that layout_width
andlayout_height
installed fill_parent
and also double-check the items above in the hierarchy. Then, if you did not help, you can correct the nine-patch. Thin black lines denote the top and left of the images that will be stretched if the content is not got a picture. Thin black lines (optional) on the right and bottom indicate the region in which the content itself will be written. Find the right position, too, turns out not once, have to experiment. Do not even think to create a nine-patch without an editor of the box, it is unnecessary removal of the brain - areas highlighted in the editor for content and errors, and even when everything seems right, do not always perceived layout infleyterom as expected.11. Add QuickActions
QuickActions - a small library for pop-up dialogs with activities such as in the figure (and not only these, because their design can be changed easily.) They have become a new popular trend of the appearance of an official twitter client. Must be other implementation, in vimeoid I use this, and it also touched up a bit for their needs.
In order to display a dialog instead of the context menu when you long to Tape element on the list, enough to override a method
onCreateContextMenu
in ListActivity
as follows:public void onCreateContextMenu (ContextMenu menu, View v, ContextMenuInfo menuInfo) { . . . final AdapterView.AdapterContextMenuInfo info = extractMenuInfo (menuInfo); final QuickAction quickAction = createQuickActions (info.position, getItem (info.position), info.targetView); if (quickAction! = null) quickAction.show (); } protected QuickAction createQuickActions (final int position, final ... item, View view) { QuickAction qa = new QuickAction (view); qa.addActionItem (getString (R.string ...), getResources (). getDrawable (R.drawable ...), new QActionClickListener () { @ Override public void onClick (View v, QActionItem item) { . . . } }); . . . return qa; }
- The directory containing the modified Library
lib-qactions
- Used in:
VideosActivity
isLibrary
see Android
in the properties of this project, and in the main project add to a project with a library point of Library
-> Add
from the same section. In this case R
-file from the project with the library will be added to the main project.12. Three more small solving
12a. A single place to call various activity
If your application aktiviti many different and they are invoked in a similar way, it might be convenient to transfer their calls including filling
Extras
in a separate class:- An example of vimeoid:
Invoke
12b. About placeholder in localization
Maybe this is obvious, but the rows of
strings.xml
pleyshodery can use to substitute some value independent of the locale inside the lines, for example: <string name="image_info">Image size: {width}x{height}</string>
. This function will format
, so you can call: format(getString(R.string.image_info), "width", String.valueOf(600), "height", String.valueOf(800))
:public static String format (String source, String ... params) { String result = source; int pos = 0; while (pos <params.length) { result = result.replaceAll ("\ \ {" + params [pos + +] + "\ \}", params [pos ++]); } return result; }
Upd. It turned out as I thought it was a bicycle: There is a standard function
getString(int resId, Object... formatArgs)
. Thank zochek .12c. About incorrect layout
Be sure to read these articles in infleyter android really very sensitive to the complex structures and if you write a complex application, leyauty sooner or later will have to optimize:My pererenderivayuschiesya leyauty often at one point and failed
getView
adapter has called almost every second (and still sometimes is, but already much less). After replacing many nested slozhnostrukturirovannyhLinearLayout
s to less invested and elegant RelativeLayout
, infleyteru was clearly easier and myself too, because the hierarchy has become shorter and make small changes to become easier. I have not yet had time to replace everywhere, but now otnoschus to leyautam carefully. Also watch out for, so thatwidth/height=wrap_content
used only possible for simple elements, the use of wrap_content
as parameters the width / height LinearLayout
and other complex types can lead to difficult consequences.