2016-06-03 14 views
5

Ist es möglich, einen Handler für setOnPageChangeListener an einen ViewPager in einer XML-Datei mit der Android Binding-Funktionalität zu binden?Android Event Listener Datenbindung für ViewPager

Die Demos zeigen onClick-Ereignisse, aber ich bin gespannt, wie viele Ereignisfunktionen ich damit implementieren kann. Alle Links auf die Fähigkeiten der Datenbindung wäre auch gut. Vielen Dank.

hypothetisches Beispiel:

example_activity.xml

<?xml version="1.0" encoding="utf-8"?> 
<layout xmlns:android="http://schemas.android.com/apk/res/android"> 

<data> 
    <variable name="handlers" type="com.example.Handlers"/> 
</data> 

<android.support.v4.view.ViewPager 
    android:id="@+id/pager" 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 

    android:onPageChangeListener="@{handlers.pageChanged}" /> 
</layout> 

Handler.java

package com.example.viewmodels; 

import android.view.View; 

public class Handlers { 
    public void pageChanged(View view){} 
} 

Die Kompilation Fehler ist:

Fehler: (6 2) Kein Ressourcenbezeichner für das Attribut 'onPageChangeListener' im Paket 'android'

Antwort

7

Es ist möglich, dies zu tun. Sie müssen einen benutzerdefinierten Bindungsadapter implementieren, da keine BindingAdapter-Klassen für View-Klassen aus Android-Unterstützungsbibliotheken vordefiniert sind.

Für die Implementierung des benutzerdefinierten Adapters können Sie this lesen.

<android.support.v4.view.ViewPager 
    android:id="@+id/pager" 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    app:onPageChangeListener="@{handlers}" /> 

Ihr BindingAapter Code:

@BindingAdapter("onPageChangeListener") 
public static void setOnPageChangeListener(ViewPager viewPager, ViewPager.OnPageChangeListener listener) { 
    Log.i("setOnPageChangeListener"); 

    // clear listeners first avoid adding duplicate listener upon calling notify update related code 
    viewPager.clearOnPageChangeListeners(); 
    viewPager.addOnPageChangeListener(listener); 
} 

P. S.

Der Code so etwas wie die unten sein sollte, ich habe sie nicht gründlich getestet Ihre übergebene Handler-Klasse sollte implementiert ViewPager.OnPageChangeListener.

+1

Nicht gute Lösung. Da ViewPager OnPageChangeListener zur internen ArrayList hinzufügt, wird durch den Aufruf von notifyChange() für diese Eigenschaft das Hinzufügen mehrerer Listener erfolgen. – b1n0m

+0

@ b1n0m aktualisiert, um Listener zuerst zu entfernen, bevor Listener hinzugefügt werden –

3

Hier ist die ultimative Lösung. Fügen Sie diese Klasse einfach in Ihr Projekt ein.

@BindingMethods({ 
    @BindingMethod(type = ViewPager.class, attribute = "android:offscreenPageLimit", method = "setOffscreenPageLimit"), 
    @BindingMethod(type = ViewPager.class, attribute = "android:adapter", method = "setAdapter"), 
    @BindingMethod(type = ViewPager.class, attribute = "android:currentPage", method = "setCurrentItem"), 
}) 
public final class ViewPagerBindingAdapter { 

    @InverseBindingAdapter(attribute = "android:currentPage", event = "android:currentPageAttrChanged") 
    public static int getCurrentPage(@NonNull final ViewPager pager) { 
     return pager.getCurrentItem(); 
    } 

    @BindingAdapter(value = {"android:onPageScrolled", "android:onPageSelected", "android:onPageScrollStateChanged", 
     "android:currentPageAttrChanged"}, requireAll = false) 
    public static void onSetAdapter(@NonNull final ViewPager pager, final OnPageScrolled scrolled, final OnPageSelected selected, 
     final OnPageScrollStateChanged scrollStateChanged, final InverseBindingListener currentPageAttrChanged) { 

     final ViewPager.OnPageChangeListener newValue; 
     if (scrolled == null && selected == null && scrollStateChanged == null && currentPageAttrChanged == null) { 
      newValue = null; 
     } else { 
      newValue = new ViewPager.OnPageChangeListener() { 
       @Override 
       public void onPageScrolled(final int position, final float positionOffset, final int positionOffsetPixels) { 
        if (scrolled != null) { 
         scrolled.onPageScrolled(position, positionOffset, positionOffsetPixels); 
        } 
       } 

       @Override 
       public void onPageSelected(final int position) { 
        if (selected != null) { 
         selected.onPageSelected(position); 
        } 
        if (currentPageAttrChanged != null) { 
         currentPageAttrChanged.onChange(); 
        } 
       } 

       @Override 
       public void onPageScrollStateChanged(final int state) { 
        if (scrollStateChanged != null) { 
         scrollStateChanged.onPageScrollStateChanged(state); 
        } 
       } 
      }; 
     } 
     final ViewPager.OnPageChangeListener oldValue = ListenerUtil.trackListener(pager, newValue, R.id.page_change_listener); 
     if (oldValue != null) { 
      pager.removeOnPageChangeListener(oldValue); 
     } 
     if (newValue != null) { 
      pager.addOnPageChangeListener(newValue); 
     } 
    } 

    public interface OnPageScrolled { 
     void onPageScrolled(int position, float positionOffset, int positionOffsetPixels); 
    } 

    public interface OnPageSelected { 
     void onPageSelected(int position); 
    } 

    public interface OnPageScrollStateChanged { 
     void onPageScrollStateChanged(int state); 
    } 

    private ViewPagerBindingAdapter() { 
     throw new UnsupportedOperationException(); 
    } 
} 

Fügen Sie auch ID-Ressource in Ihren Ressourcen hinzu.

<resources> 
    <item name="page_change_listener" type="id"/> 
</resources> 

Dann werden Sie in der Lage sein, es in XML zu verwenden wie:

<android.support.v4.view.ViewPager 
       android:layout_width="match_parent" 
       android:layout_height="match_parent" 
       android:currentPage="@={viewModel.currentPage}" 
       android:offscreenPageLimit="@{viewModel.offscreenPageLimit}" 
       android:onPageSelected="@{currentPage -> viewModel.pageSelected(currentPage)}" 
       android:adapter="@{adapter}"/> 

Wie Sie sehen können, currentPage inverse Bindung hat, so dass Ihr Ansichtsmodell wird in der Lage sein, aktuelle Seite zu setzen und auch Strom bekommen Seite, wenn der Benutzer wischt.

+0

sind diese Einstellungen in der Lage, Fragment in Viewpager dynamisch zu aktualisieren? in Echtzeit? –

+0

@ azizi-musa, können Sie den Wert 'viewModel.currentPage' einstellen, um eine andere Seite auszuwählen. Wenn Sie die Fragmente in 'ViewPager' ändern wollen, sollten Sie dies in' PagerAdapter' tun. – b1n0m