Monday, October 14, 2013

InfiniteViewPager 0.4 released

The InfiniteViewPager version 0.4 has been released. It now has an OnInfinitePageChangeListener. You can use it by doing the following:

InfiniteViewPager viewPager = ...;
viewPager.setOnInfinitePageChangeListener(new InfiniteViewPager.OnInfinitePageChangeListener() {
  @Override
  public void onPageScrolled(Object indicator, float positionOffset, int positionOffsetPixels) {
  }

  @Override
  public void onPageSelected(Object indicator) {
  }

  @Override
  public void onPageScrollStateChanged(int state) {
  }
});
In general it is not very different from the regular ViewPager.OnPageChangeListener. The callback methods
onPageSelected(final Object indicator)
and
onPageScrolled(Object indicator, float positionOffset, int positionOffsetPixels)
return the current indicator instead of position.

You can bind it with maven:

        <dependency>
            <groupId>com.thehayro</groupId>
            <artifactId>infiniteviewpager</artifactId>
            <version>0.4</version>
            <type>apklib</type>
        </dependency>
 
The code can be found here. 

Thursday, September 26, 2013

InfiniteViewPager - an infinite paging ViewPager

Hi,

a couple of months ago, I wrote an article about enabling infinite paging with the android ViewPager. In my opinion, the proposed solution was rather simple and hacky, since it only changed the text of each page. However with that approach, I finally found the time to do a custom ViewPager widget that enables the infinite paging feature with simple and complex page layouts rather than changing the text of the EditText widget. Here is a video showing the InfiniteViewPager in action.

Including the InfiniteViewPager

Binding the InfiniteViewPager into your application is not different than binding a regular ViewPager. You include it with your xml layout with the following:


<com.thehayro.view.InfiniteViewPager 
    android:id="@+id/infinite_viewpager"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"/>

Using the adapter

Like the regular ViewPager correspondents with its adapter class, the InfiniteViewPager also has its custom adapter called InfinitePagerAdapter. Before this is explained any further, I would like to give some understanding on how it works and what differs from a regular PagerAdapter implementation.
The main difference between a regular PagerAdapter (as we know) and the InfinitePagerAdapter are the number of pages. When implementing a PagerAdapter, one important method you have to implement is getCount(). With that, you tell the ViewPager how many pages you want. When implementing an InfinitePagerAdapter, setting the maximum number of pages is not necessary. The number of scrolling pages is indefinite. This leads to a certain consequence: how is it possible to differentiate one page from another? The regular ViewPager indicates its pages by its index. Methods like ViewPager.setCurrentItem(int index) help to switch to a certain page. For almost every use case, the page index indication is helpful for a finite space. But what about other indicators? For instance a string, where the indication is the alphabet or a concatenation of characters. How would integer index be mapped on a string datastructure or any other class that does not necessarily implement the Comparable interface? The InfinitePagerAdapter provides a solution to that issue. This is shown in the following code:

public abstract class InfinitePagerAdapter<T> extends PagerAdapter {

    /**
     * Standard constructor.
     * @param initValue the initial indicator value the ViewPager
     * should start with.
     */
    public InfinitePagerAdapter(T initValue);

    /**
     *
     * @return the next indicator.
     */
    public abstract T getNextIndicator();

    /**
     *
     * @return the previous indicator.
     */
    public abstract T getPreviousIndicator();

    /**
     * Instantiates a page.
     * @param indicator the indicator the page should be instantiated with.
     * @return a ViewGroup containing the page layout.
     */
    public abstract ViewGroup instantiateItem(T indicator);
}

The InfinitePagerAdapter gives the possibility to set an indicator type. Since the indicator can be any  datatype, it is necessary to implement the getNextIndicator() and getPreviousIndicator() Methods to define the next and previous indicator. This gives a certain flexibility towards the page indication. A simple implementation of the InfinitePagerAdapter can look like this:


public class MyInfinitePagerAdapter<Integer> extends InfinitePagerAdapter {


    public Integer getNextIndicator() {
        // getCurrentIndicator is a protected method.
        return getCurrentIndicator() + 1;
    }


    public Integer getPreviousIndicator() {
        // getCurrentIndicator is a protected method.
        return getCurrentIndicator() - 1;
    }

    public ViewGroup instantiateItem(Integer indicator) {
        // space for rent
        return null;
    }
}

And instantiate it with something like this:
// 0 is the initial type
MyInfinitePagerAdapter<Integer> myAdapter = new MyInfinitePagerAdapter<Integer>(0);

Restoring states on device rotation

The above listed code is the minimum required implementations a developer has to provide in order to use the InfiniteViewPager. However, this has a certain limitation: rotating the device shows the initial page and the current page is lost, due to android's layout reinitialization. This can be solved by furthermore overriding two methods:

String getStringRepresentation(final T currentIndicator);


T convertToIndicator(final String representation);


The first method converts the current indicator into a string representation, and the second does the opposite. These methods provide the InfiniteViewPager to restore its state when the device is rotated.

Binding in your project

You can bind the InfiniteViewPager with maven:

        <dependency>
            <groupId>com.thehayro</groupId>
            <artifactId>infiniteviewpager</artifactId>
            <version>0.3</version>
            <type>apklib</type>
        </dependency>

The code can be found here.

Sunday, July 28, 2013

SeesawView - a visual feedback for flat view interaction

Introduction

Touchscreens enable the direct pointing on the screen contents but also take away the haptic feedback of a keyboard or telephone keypad (if it is a smartphone). On smartphones, a common solution is to give a vibrotactile and (a more than desktop computers) visual feedback when users interact with the user interface. For example a button has different states (pressed, released, disabled etc.). With that information the smartphone user interface gives us the visual (and sometimes haptic) feedback that the user interface component (e.g. button) has been interacted with (see figure 1).
Figure 1: A button that gets interacted with and its visual feedback.

However there are components (e.g. images) that do not have states to indicate the visual feedback when interacting with them (e.g. pressing). I call these components flat components, because the do not have a visual "depth" in comparison of the button. A button implements a to-be-pressed metaphor, whereas an image does not. But there are use cases when an image should be interacted with. Examples are zooming, showing in full screen etc. 
This post introduces an android view container that gives visual feedback for these described flat views. 

SeesawView

The SeesawView a simple view container that seesaws a containing view. Here is a simple video showing the seesawView in action.


The code can be downloaded here.

Sidenote: The view only works properly when the childview has an actual interaction set (e.g. setOnClickListener())

cheers

Tuesday, June 25, 2013

Android: The usability papercut of the paste popup

Introduction

Since Android gingerbread (v. 2.3), Google enhanced the copy/paste functionality by pressing and holding a selected word to copy text to the clipboard[1]. In more recent Android versions the copy and paste functionality is shown in a popup window rather than a context menu as known in gingerbread.

This post is about the definition of an usability papercut that concerns the paste popup window. It is divided into two sections. In the first section I will describe a scenario and in the second section I will talk and discuss about the papercut itself.

Scenario

To illustrate the problem, I wrote a simple application that shows an EditText widget with some entered text as seen in Figure 1.

Figure 1: EditText with entered sample text.


To let the paste popup appear, one just do a press hold on the text. This triggers two operations: a) the placement of the caret and b) the appearance of the popup. In figure 2 it is shown that the popup window is located right above the caret and the press position (white circle).

Figure 2: Appearance of the paste/replace popup.

The papercut

The papercut happens when there is no text in the EditText widget. As seen in figure 3 the touch-hold position and the popup window are "dislocated" in comparison to figure 2.

Figure 3: The papercut in action.

To argue against this dislocation, one can say that the location of the popup follows the law of proximity, which says that "things that are close together as seen as belonging together"[2]. As seen in figure 2 and 3 the paste (and replace) popup appears right above the caret indicating the user that there is a possibility to paste (or replace) some text right where the caret is positioned. However in figure 3 the paste popup appears at an unexpected location, so that one has to visually search for the popup window. The solution I am proposing is to place the popup window right above the pressed hold position of the finger when there is no entered text.

To justify the argument of dislocation, I tried to apply FFitt's Law[2] (an expansion of Fitt's law for finger touch input) and to calculate the mean time to complete the paste task with and without entered text. However this was not possible, because the popup window is not available in the android view hierarchy of the sample app as seen in figure 4.

Figure 4: View hierarchy of the sample app. The red frame highligts the curent view of the sample app. The paste popup is not part of the view hierarchy.




The paste functionality is located in the TextView class (from which the EditText class inherits). In detail, the paste function and management of positioning the caret etc. is outsourced into the Editor class. This class uses internal android classes (e.g. classes that are located in the com.android.internal.* package), which makes it even more difficult to change the popup location as suggested above.

Conclusion

In this post I introduced an usability papercut concerning the paste popup. I wrote a simple application that illustrates the problem. Moreover did I discuss why and how this is a papercut with a suggestion on how to fix it. An attempt on how fix this in code has been introduced as well. I found out that a popup window cannot be captured with the hierarchyviewer, since it is not a part of the view tree. It would be interesting to see a user interface component that is like the popup window but also part of the view hierarchy.

References

[1] Android Gingerbread Platform Hightlights. Available: http://developer.android.com/about/versions/android-2.3-highlights.html. Last accessed 25th June 2013.
[2] Bi, Xiaojun and Li, Yang and Zhai, Shumin. (2013). FFitts law: modeling finger touch with fitts' law. Proceedings of the SIGCHI Conference on Human Factors in Computing Systems. CHI '13 , 1363-1372.
[3] Boeree, George. (2009). Perception and Interaction. Available: http://webspace.ship.edu/cgboer/genpsyperception.html. Last accessed 25th June 2013.

Saturday, December 1, 2012

Enable infinite paging with Android ViewPager

UPDATE

I wrote an infinite ViewPager library that you can include in your project.
--------

Hi,

It's been a while since I blogged about something. This post is about enabling infinite paging with the Android ViewPager View. The question on how to do this was initially a question of mine on stackoverflow. Since some people asked me to publish some code, I thought I'd rather blog about this.

Note that this way has been worked for me. If you have any other way to do that, I'd be happy if you would share it.

In the stackoverflow thread I stated [1], that it is necessary to import the source files of the android v4 support library, because (at that time) there had to be a slight adjustment in the ViewPager Method. Now I found out that this is not necessary anymore (the adjustment was already there when I looked into the code. Looks like the android developers changed it).

So, all you need is to include the android support library to your project.
The result will look like this:

Page "-15"

Page "0"












Now let's get to the coding part. First of, we create a layout xml, which includes the ViewPager View.

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >
    <android.support.v4.view.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</FrameLayout>

Then we create another xml for the page content. Let's call it content.xml.


<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/textview"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
</TextView>

For each page, we need a model class that holds the content of each page. We name the class PageModel. Note that we are also holding the view of the page (the TextView) that will be displayed to the user. This enables us to do the background content manipulation.

public class PageModel {

 private int index;
 private String text;
 public TextView textView;

 
 public PageModel(int index) {
  this.index = index;
  setIndex(index);
 }

 public int getIndex() {
  return index;
 }

 public void setIndex(int index) {
  this.index = index;
  setText(index);
 }

 public String getText() {
  return text;
 }

 private void setText(int index) {
  this.text = String.format("Page %s", index);
 }
}

Now we prepared the model for the paging. The following class manages the pages and the background content manipulation and switching the pages in a way that the user thinks that there are infinite pages.

Now we create an avitiy calles MainActivity:

public class MainActivity extends Activity {

 
 // we name the left, middle and right page
 private static final int PAGE_LEFT = 0;
 private static final int PAGE_MIDDLE = 1;
 private static final int PAGE_RIGHT = 2; 
 
 
 private LayoutInflater mInflater;
 private int mSelectedPageIndex = 1;
// we save each page in a model
 private PageModel[] mPageModel = new PageModel[3];
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  // initializing the model
  initPageModel();
  
  mInflater = getLayoutInflater();
  MyagerAdaper adapter = new MyagerAdaper();
    
  final ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);
  viewPager.setAdapter(adapter);
  // we dont want any smoothscroll. This enables us to switch the page
  // without the user notifiying this
  viewPager.setCurrentItem(PAGE_MIDDLE, false);
  
  viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
   
   @Override
   public void onPageSelected(int position) {
    mSelectedPageIndex = position;
   }
   
   @Override
   public void onPageScrolled(int arg0, float arg1, int arg2) { }
   
   @Override
   public void onPageScrollStateChanged(int state) {
    if (state == ViewPager.SCROLL_STATE_IDLE) {
     
     final PageModel leftPage = mPageModel[PAGE_LEFT];
     final PageModel middlePage = mPageModel[PAGE_MIDDLE];
     final PageModel rightPage = mPageModel[PAGE_RIGHT];
     
     final int oldLeftIndex = leftPage.getIndex();
     final int oldMiddleIndex = middlePage.getIndex();
     final int oldRightIndex = rightPage.getIndex();
     
     // user swiped to right direction --> left page
     if (mSelectedPageIndex == PAGE_LEFT) {
       
      // moving each page content one page to the right
      leftPage.setIndex(oldLeftIndex - 1);
      middlePage.setIndex(oldLeftIndex);
      rightPage.setIndex(oldMiddleIndex);
      
      setContent(PAGE_RIGHT);
      setContent(PAGE_MIDDLE);
      setContent(PAGE_LEFT);
      
     // user swiped to left direction --> right page
     } else if (mSelectedPageIndex == PAGE_RIGHT) {

      leftPage.setIndex(oldMiddleIndex);      
      middlePage.setIndex(oldRightIndex);
      rightPage.setIndex(oldRightIndex + 1);

      setContent(PAGE_LEFT);
      setContent(PAGE_MIDDLE);
      setContent(PAGE_RIGHT);
     }
     viewPager.setCurrentItem(PAGE_MIDDLE, false);
    }
   }
  });
 }
 
 private void setContent(int index) {
  final PageModel model =  mPageModel[index];
  model.textView.setText(model.getText());
 }

 private void initPageModel() {
  for (int i = 0; i < mPageModel.length; i++) {
   // initing the pagemodel with indexes of -1, 0 and 1
   mPageModel[i] = new PageModel(i - 1);
  }  
 }

 private class MyagerAdaper extends PagerAdapter {

  @Override
  public int getItemPosition(Object object) {
   return POSITION_NONE;
  }

  @Override
  public void destroyItem(ViewGroup container, int position, Object object) {
   container.removeView((View) object);
  }

  @Override
  public int getCount() {
   // we only need three pages
   return 3;
  }

  @Override
  public Object instantiateItem(ViewGroup container, int position) {
   TextView textView = (TextView)mInflater.inflate(R.layout.content, null);
   PageModel currentPage = mPageModel[position];
   currentPage.textView = textView;
   textView.setText(currentPage.getText());
   container.addView(textView);
   return textView;
  }

  @Override
  public boolean isViewFromObject(View view, Object obj) {
   return view == obj;
  }
 }
The "magic" for infinite paging happens at the onPageScrollStateChanged event. Each time when a page change occurs, we move the content to the right (or left depending on the page the user changed  to), change the content of each page (with the method setContent()) and switch again to the middle page. This enables the user to change the page (either to the left or right) again.

Of course the background content manipulation has been kept very simple. Moving more complex content from one page to another requires moving the childviews of a ViewGroup.

Hope it helped. And of course, the code can be downloaded here.

Happy coding

References
[1] Original stackoverflow question - http://stackoverflow.com/questions/7766630/changing-viewpager-to-enable-infinite-page-scrolling

Inspiring app that helped me writing the solution http://code.google.com/p/electricsleep/source/browse/trunk/src/com/androsz/electricsleepbeta/app/HistoryMonthActivity.java

Saturday, March 3, 2012

Extending interaction of views with gestures

Hi,
in this post i will talk about extending the interaction of an existing view with gestures. For example, you have a view that provides specific interaction like a ListView providing  you to select an element, or an EditText allowing you to enter text.
I will use an EditText widget and extend it with a swiping gesture to remove its content (screenshot below).

Okay, let's get started. First of all we create a simple
swipe-from-right-to-left gesture with the Gestures Builder (a good tutorial on how to add gestures in Android can be found here). We will call the gesture "clear_text".

After that is done, we will have an XML called "swipe_edit_text.xml" that describes the layout of our view.


<?xml version="1.0" encoding="utf-8"?>  
 <android.gesture.GestureOverlayView xmlns:android="http://schemas.android.com/apk/res/android"  
   android:id="@+id/gesture_overlay"  
   android:layout_width="match_parent"  
   android:layout_height="match_parent"  
   android:eventsInterceptionEnabled="true"  
   android:uncertainGestureColor="@android:color/transparent" >  
   <EditText  
     android:id="@+id/edit_text"  
     android:layout_width="match_parent"  
     android:layout_height="wrap_content" />  
 </android.gesture.GestureOverlayView>  

Important here is the line below to still allow the usual interaction of our view we are extending.
   android:eventsInterceptionEnabled="true"

Next we come to the code. Create a class SwipableEditText.
public class SwipableEditText extends FrameLayout {

 
 // the library to load the gestures
 private GestureLibrary mLibrary;

 // the gesture overlay
 private GestureOverlayView mGestureOverlay;
        // the view we are extending with gestures
 private EditText mEditText;
 
 public SwipableEditText(Context context) {
  this(context, null);
 }

 public SwipableEditText(Context context, AttributeSet attrs) {
  this(context, attrs, 0);
 }

 public SwipableEditText(Context context, AttributeSet attrs, int defStyle) {
  super(context, attrs, defStyle);
  
  // inflate the layout
  final LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
  FrameLayout li = (FrameLayout) inflater.inflate(R.layout.swipe_edit_text, this);
  mGestureOverlay = (GestureOverlayView) li.findViewById(R.id.gesture_overlay);
  mEditText = (EditText) li.findViewById(R.id.edit_text);
  
  // load the gestures
  mLibrary = GestureLibraries.fromRawResource(context, R.raw.gestures);
  if (!mLibrary.load())
   return;
  init();
 }
 
 private void init() {
  mGestureOverlay.addOnGesturePerformedListener(new GestureOverlayView.OnGesturePerformedListener() {
   

   @Override
   public void onGesturePerformed(GestureOverlayView overlay, Gesture gesture) {

    ArrayList predictions = mLibrary.recognize(gesture);
    for (Prediction p : predictions) {
     // gesture accepted
     if (p.score > 1.0) {
       // user performed swipe gesture
       if (p.name.equalsIgnoreCase("clear_text")) {
        mEditText.setText("");
       }
      }
     }
    }
  });
 }
}

The swipe gesture performs the deletion of the text in the EditText Widget.
And that's it. Now you can integrate your view into any activity.
The code can be downloaded here.

Happy Coding.

Wednesday, August 24, 2011

QML in NotePad++

Here is a little file that enables syntax highlighting in the NotePadd++ editor for Windows.
Just copy the content into your APPDATA\Notepad++\userDefineLang.xml or simply create it if it isn't there. If you create it, make sure to firstly write
<NotepadPlus> </NotepadPlus>
and the content between those two brackets (please consider the case sensitivity).
Here's a little screenshot programming QML in Notepad++: