📜 ⬆️ ⬇️

Very simple screen slider

When I started to learn android, I really wanted to add a beautiful flipping screens - as in popular applications. I just wanted some semblance of LinearLayout, the contents of which can be scrolled. However, in order to write a simple slider using ViewPager, it took me a few hours to parse, as well as a whole page of code. In addition, there were some restrictions - adding an object inside the <ViewPager> tag did not add it to the list of widgets.

Having understood, I decided to write my own ScreenPager class, which is inherited from ViewPager and works very simply. And it works in both xml and pure code.



Using

To start, create a simple application. Let its MainActivity class be in com.example.testapp and have an onCreate method. Now create the ScreenPager class in the same directory and add the following code there, without delving into it for now:
')
ScreenPager.java
package com.example.testapp; import android.content.Context; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import java.util.ArrayList; import java.util.List; public class ScreenPager extends ViewPager { private PagerAdapter adapter = new ScreenPagerAdapter(); private List<View> viewList = new ArrayList<View>(); public ScreenPager(Context context) { super(context); init(); } public ScreenPager(Context context, AttributeSet attrs) { super(context, attrs); init(); } @Override public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { return new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); } private void init() { setAdapter(adapter); } @Override public void addView(View child, ViewGroup.LayoutParams params) { addScreen(child); } public void addScreen(View screen) { viewList.add(screen); adapter.notifyDataSetChanged(); } private class ScreenPagerAdapter extends PagerAdapter { @Override public int getCount() { return viewList.size(); } @Override public boolean isViewFromObject(View view, Object o) { return view.equals(o); } @Override public Object instantiateItem(ViewGroup container, int position) { View view = viewList.get(position); container.addView(view); return view; } @Override public void destroyItem(ViewGroup container, int position, Object object) { container.removeView((View) object); } } /** * Use - in MainActivity.onCreate add: * LayoutInflater.from(this).setFactory(ScreenPager.getShortNameFactory()); * * @return */ static LayoutInflater.Factory getShortNameFactory() { return new LayoutInflater.Factory() { @Override public View onCreateView(String name, Context context, AttributeSet attrs) { if (ScreenPager.class.getSimpleName().equals(name)) { return new ScreenPager(context, attrs); } return null; } }; } } 

Now you can create a screenpager.xml file in your res / layout and put the following code there

 <?xml version="1.0" encoding="utf-8"?> <com.example.testapp.ScreenPager xmlns:a="http://schemas.android.com/apk/res/android" a:layout_width="match_parent" a:layout_height="match_parent"> <Button a:text=" № 1" /> <Button a:text=" № 2" /> <Button a:text=" № 3" /> <Button a:text=" № 4" /> </com.example.testapp.ScreenPager> 

If you manually create a file, do not forget to save it in UTF-8 encoding. And do not forget that strings should be stored in string resources, and not inserted directly. In this article, I intentionally did not do this in order to simplify the code.

Now add one line to MainActivity.onCreate

 setContentView(R.layout.screenpager); 

Everything! Slider works. But the long name com.example.testapp is ugly. We want to make everything as simple as possible, right?

It turned out that the system allows using short names only for classes from android.view or android.widget. If we want to do this for our class, we should override the Factory for the LayoutInflater. I wrote the getShortNameFactory method, which returns a special factory that allows you to use an abbreviated name for our class. Install it in the onCreate method, and you need to do this before setContentView:

 LayoutInflater.from(this).setFactory(ScreenPager.getShortNameFactory()); 

Also import LayoutInflater if your IDE does not do it for you:

 import android.view.LayoutInflater; 

Now we can use use the abbreviated name of our class:

 <ScreenPager xmlns:a="http://schemas.android.com/apk/res/android" a:layout_width="match_parent" a:layout_height="match_parent"> <Button a:text=" № 1" /> <Button a:text=" № 2" /> <Button a:text=" № 3" /> <Button a:text=" № 4" /> </ScreenPager> 

But that is not all! You can build your screens in code, and this is also simple:

 ScreenPager screenPager = new ScreenPager(this); Button button1 = new Button(this); button1.setText(" №1"); screenPager.addScreen(button1); Button button2 = new Button(this); button2.setText(" №2"); screenPager.addScreen(button2); //... setContentView(screenPager); 

Use on health. Instead of buttons, you can insert your layout managers or any other is-a View objects. True, your development environment (in my case, this is Android Studio) can swear at the lack of layout_width and layout_height, but this warning can be easily disabled.

I post the full source code of the example.

Details

How ViewPager and PagerAdapter work, you can read in this article . Actually, I studied them for her.

The ScreenPager class is inherited from ViewPager and includes a non-static nested class - ScreenPagerAdapter, which is inherited from the PagerAdapter. If you need to get it, it can be done through getAdapter (). Also inside ScreenPager is a list from View, with which it should work.

When the xml-code is processed, addView is invoked for each object inside the ScreenPager tag. We redefined this function and it redirects the View to addScreen to add it to the collection and notify the adapter about it. Thus, both cases — the creation of the slider through the code, and through the xml — pass through addScreen.

Next, I added a small static function that returns the LayoutInflater.Factory. We simply check the short name of our class - how much it coincides with the name of the tag - and, if successful, we return our object.

And now the fun part. You probably noticed that, for example, LinearLayout forces all children to specify layout_width and layout_height. And GridLayout does not require this. I really wanted to free the users of my widget from such redundant code, in this case also not meaningful. I had to read a lot about the creation of custom container components. But there was no necessary information. And only a study of the source code GridLayout answered this question. It turns out that it is enough to override generateLayoutParams and return ViewGroup.LayoutParams (LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT). And this is all - now all child widgets can be specified without height and width attributes.

Finally, I will talk about one interesting feature that I stumbled upon. You can achieve reduced use of ScreenPager without LayoutInflater.from (this) .setFactory. Let's reason logically. Android searches for abbreviated names in the android.view package. Actually, nothing prevents us in our src folder from creating the android folder on a par with the com folder. And it already has a folder view and place the file ScreenPager.java there, not forgetting to change the name of the package inside the file itself. And it compiles perfectly and runs! Try it. However, putting your classes in other people's packages is not a good approach. Rather just an interesting moment.

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


All Articles