├── demo
├── .gitignore
├── src
│ └── main
│ │ ├── res
│ │ ├── values
│ │ │ ├── strings.xml
│ │ │ ├── colors.xml
│ │ │ └── styles.xml
│ │ ├── drawable
│ │ │ ├── tu.jpg
│ │ │ ├── text_view_bg.xml
│ │ │ └── ic_launcher_background.xml
│ │ ├── mipmap-hdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── xml
│ │ │ └── network_security_config.xml
│ │ ├── mipmap-anydpi-v26
│ │ │ ├── ic_launcher.xml
│ │ │ └── ic_launcher_round.xml
│ │ ├── layout
│ │ │ ├── block_state_text_view.xml
│ │ │ ├── block_focus_state_view.xml
│ │ │ ├── title_view.xml
│ │ │ ├── activity_main.xml
│ │ │ ├── block_image.xml
│ │ │ ├── footer_view.xml
│ │ │ └── block_loading_state.xml
│ │ └── drawable-v24
│ │ │ └── ic_launcher_foreground.xml
│ │ ├── java
│ │ └── com
│ │ │ └── msisuzney
│ │ │ └── tv
│ │ │ └── demo
│ │ │ ├── bean
│ │ │ ├── ColumnFocusStateBean.java
│ │ │ ├── RecyclerViewStateBean.java
│ │ │ ├── FooterBean.java
│ │ │ ├── TitleBean.java
│ │ │ └── TabBean.java
│ │ │ ├── MyStateChangedObserver.java
│ │ │ ├── MyStateChangeObservable.java
│ │ │ ├── MainActivity.java
│ │ │ ├── viewfactory
│ │ │ ├── presenter
│ │ │ │ ├── FooterViewPresenter.java
│ │ │ │ ├── ColumnFocusChangeListenerTextViewPresenter.java
│ │ │ │ ├── TitlePresenter.java
│ │ │ │ ├── StateTextViewPresenter.java
│ │ │ │ ├── ImageViewPresenter.java
│ │ │ │ └── ImageViewPresenter2.java
│ │ │ └── ColumnItemViewFactory.java
│ │ │ ├── view
│ │ │ ├── StateTextView.java
│ │ │ └── ColumnFocusChangeListenerTextView.java
│ │ │ └── WaterfallFragment.java
│ │ ├── AndroidManifest.xml
│ │ └── assets
│ │ └── data.json
├── proguard-rules.pro
└── build.gradle
├── waterfallayout
├── .gitignore
├── src
│ └── main
│ │ ├── res
│ │ ├── values
│ │ │ └── strings.xml
│ │ └── layout
│ │ │ ├── hgv.xml
│ │ │ └── fragment_waterfall.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ └── com
│ │ └── msisuzney
│ │ └── tv
│ │ └── waterfallayout
│ │ ├── OnItemKeyListener.java
│ │ ├── OnRowSelectedListener.java
│ │ ├── model
│ │ ├── Item.java
│ │ ├── ColumnLayoutCollection.java
│ │ ├── HorizontalLayoutItem.java
│ │ ├── HorizontalLayoutCollection.java
│ │ ├── Collection.java
│ │ └── ColumnLayoutItem.java
│ │ ├── leanback
│ │ ├── ViewHolderTask.java
│ │ ├── PresenterSelector.java
│ │ ├── FacetProvider.java
│ │ ├── FacetProviderAdapter.java
│ │ ├── OnChildLaidOutListener.java
│ │ ├── SinglePresenterSelector.java
│ │ ├── OnChildSelectedListener.java
│ │ ├── ItemAlignment.java
│ │ ├── OnChildViewHolderSelectedListener.java
│ │ ├── VerticalGridView.java
│ │ ├── package-info.java
│ │ ├── ItemAlignmentFacetHelper.java
│ │ ├── ArrayObjectAdapter.java
│ │ ├── ItemAlignmentFacet.java
│ │ ├── SingleRow.java
│ │ ├── ViewsStateBundle.java
│ │ ├── Presenter.java
│ │ ├── ObjectAdapter.java
│ │ └── WindowAlignment.java
│ │ ├── StateChangedObserver.java
│ │ ├── presenter
│ │ ├── RowPresenterSelector.java
│ │ ├── ColumnLayoutRowPresenter.java
│ │ └── HorizontalLayoutRowPresenter.java
│ │ ├── view
│ │ ├── ColumnFocusChangeListener.java
│ │ ├── ColumnLayout.java
│ │ └── FocusLineFeedFrameLayout.java
│ │ ├── StateChangeObservable.java
│ │ └── RowsFragment.java
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── demo.gif
├── mpv.png
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── LICENSE
├── gradlew.bat
├── README.md
└── gradlew
/demo/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | **/.DS_Store
--------------------------------------------------------------------------------
/waterfallayout/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | **/.DS_Store
3 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':demo', ':waterfallayout'
2 |
--------------------------------------------------------------------------------
/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msisuzney/tv-waterfall-layout/HEAD/demo.gif
--------------------------------------------------------------------------------
/mpv.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msisuzney/tv-waterfall-layout/HEAD/mpv.png
--------------------------------------------------------------------------------
/demo/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | TV_waterfallayout
3 |
4 |
--------------------------------------------------------------------------------
/demo/src/main/res/drawable/tu.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msisuzney/tv-waterfall-layout/HEAD/demo/src/main/res/drawable/tu.jpg
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msisuzney/tv-waterfall-layout/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/waterfallayout/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Waterfall
3 |
4 |
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msisuzney/tv-waterfall-layout/HEAD/demo/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msisuzney/tv-waterfall-layout/HEAD/demo/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msisuzney/tv-waterfall-layout/HEAD/demo/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msisuzney/tv-waterfall-layout/HEAD/demo/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msisuzney/tv-waterfall-layout/HEAD/demo/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msisuzney/tv-waterfall-layout/HEAD/demo/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msisuzney/tv-waterfall-layout/HEAD/demo/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msisuzney/tv-waterfall-layout/HEAD/demo/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msisuzney/tv-waterfall-layout/HEAD/demo/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msisuzney/tv-waterfall-layout/HEAD/demo/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/waterfallayout/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/demo/src/main/res/xml/network_security_config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/demo/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #008577
4 | #00574B
5 | #D81B60
6 |
7 |
--------------------------------------------------------------------------------
/demo/src/main/java/com/msisuzney/tv/demo/bean/ColumnFocusStateBean.java:
--------------------------------------------------------------------------------
1 | package com.msisuzney.tv.demo.bean;
2 |
3 | /**
4 | * 栏目中监听栏目是否获得焦点的Bean
5 | * @author: chenxin
6 | * @date: 2020-02-29
7 | * @email: chenxin7930@qq.com
8 | */
9 | public class ColumnFocusStateBean {
10 | }
11 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Nov 26 20:24:23 CST 2019
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
7 |
--------------------------------------------------------------------------------
/demo/src/main/java/com/msisuzney/tv/demo/bean/RecyclerViewStateBean.java:
--------------------------------------------------------------------------------
1 | package com.msisuzney.tv.demo.bean;
2 |
3 | /**
4 | * 栏目中监听RecyclerView状态的Bean
5 | * @author: chenxin
6 | * @date: 2019-12-21
7 | * @email: chenxin7930@qq.com
8 | */
9 | public class RecyclerViewStateBean {
10 | }
11 |
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/demo/src/main/res/drawable/text_view_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/demo/src/main/java/com/msisuzney/tv/demo/MyStateChangedObserver.java:
--------------------------------------------------------------------------------
1 | package com.msisuzney.tv.demo;
2 |
3 |
4 | import com.msisuzney.tv.waterfallayout.StateChangedObserver;
5 |
6 | /**
7 | * @author: chenxin
8 | * @date: 2019-12-20
9 | * @email: chenxin7930@qq.com
10 | */
11 | public interface MyStateChangedObserver extends StateChangedObserver {
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/demo/src/main/java/com/msisuzney/tv/demo/MyStateChangeObservable.java:
--------------------------------------------------------------------------------
1 | package com.msisuzney.tv.demo;
2 |
3 |
4 | import com.msisuzney.tv.waterfallayout.StateChangeObservable;
5 |
6 | /**
7 | * @author: chenxin
8 | * @date: 2019-12-20
9 | * @email: chenxin7930@qq.com
10 | */
11 | public class MyStateChangeObservable extends StateChangeObservable {
12 | }
13 |
--------------------------------------------------------------------------------
/demo/src/main/java/com/msisuzney/tv/demo/bean/FooterBean.java:
--------------------------------------------------------------------------------
1 | package com.msisuzney.tv.demo.bean;
2 | /**
3 | * @author: chenxin
4 | * @date: 2019-12-20
5 | * @email: chenxin7930@qq.com
6 | */
7 | public class FooterBean {
8 |
9 | private String text;
10 |
11 | public String getText() {
12 | return text;
13 | }
14 |
15 | public void setText(String text) {
16 | this.text = text;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/waterfallayout/src/main/java/com/msisuzney/tv/waterfallayout/OnItemKeyListener.java:
--------------------------------------------------------------------------------
1 | package com.msisuzney.tv.waterfallayout;
2 |
3 | import android.view.KeyEvent;
4 | import android.view.View;
5 | /**
6 | * @author: chenxin
7 | * @date: 2019-12-20
8 | * @email: chenxin7930@qq.com
9 | */
10 | public interface OnItemKeyListener {
11 | void onClick(Object item);
12 |
13 | boolean onKey(View v, KeyEvent event, Object item);
14 | }
15 |
--------------------------------------------------------------------------------
/waterfallayout/src/main/java/com/msisuzney/tv/waterfallayout/OnRowSelectedListener.java:
--------------------------------------------------------------------------------
1 | package com.msisuzney.tv.waterfallayout;
2 |
3 | /**
4 | * @author: chenxin
5 | * @date: 2019-12-20
6 | * @email: chenxin7930@qq.com
7 | */
8 |
9 | import com.msisuzney.tv.waterfallayout.leanback.OnChildViewHolderSelectedListener;
10 |
11 | /**
12 | * 选中某行
13 | */
14 | public class OnRowSelectedListener extends OnChildViewHolderSelectedListener {
15 | }
16 |
--------------------------------------------------------------------------------
/demo/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/waterfallayout/src/main/java/com/msisuzney/tv/waterfallayout/model/Item.java:
--------------------------------------------------------------------------------
1 | package com.msisuzney.tv.waterfallayout.model;
2 |
3 | /**
4 | * @author: chenxin
5 | * @date: 2019-12-20
6 | * @email: chenxin7930@qq.com
7 | */
8 | public abstract class Item {
9 |
10 | //具体的数据
11 | private Object data;
12 |
13 | public Object getData() {
14 | return data;
15 | }
16 |
17 | public void setData(Object data) {
18 | this.data = data;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/demo/src/main/res/layout/block_state_text_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/waterfallayout/src/main/res/layout/hgv.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/demo/src/main/java/com/msisuzney/tv/demo/bean/TitleBean.java:
--------------------------------------------------------------------------------
1 | package com.msisuzney.tv.demo.bean;
2 | /**
3 | * @author: chenxin
4 | * @date: 2019-12-20
5 | * @email: chenxin7930@qq.com
6 | */
7 | public class TitleBean {
8 | private String title;
9 |
10 | public TitleBean(String title) {
11 | this.title = title;
12 | }
13 |
14 | public String getTitle() {
15 | return title;
16 | }
17 |
18 | public void setTitle(String title) {
19 | this.title = title;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/demo/src/main/java/com/msisuzney/tv/demo/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.msisuzney.tv.demo;
2 |
3 | import androidx.appcompat.app.AppCompatActivity;
4 | import android.os.Bundle;
5 | /**
6 | * @author: chenxin
7 | * @date: 2019-12-20
8 | * @email: chenxin7930@qq.com
9 | */
10 | public class MainActivity extends AppCompatActivity {
11 |
12 |
13 | @Override
14 | protected void onCreate(Bundle savedInstanceState) {
15 | super.onCreate(savedInstanceState);
16 | setContentView(R.layout.activity_main);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/demo/src/main/res/layout/block_focus_state_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/waterfallayout/src/main/res/layout/fragment_waterfall.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
--------------------------------------------------------------------------------
/demo/src/main/res/layout/title_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
--------------------------------------------------------------------------------
/demo/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
--------------------------------------------------------------------------------
/waterfallayout/src/main/java/com/msisuzney/tv/waterfallayout/model/ColumnLayoutCollection.java:
--------------------------------------------------------------------------------
1 | package com.msisuzney.tv.waterfallayout.model;
2 |
3 | import java.util.List;
4 |
5 | public final class ColumnLayoutCollection extends Collection {
6 |
7 |
8 | public ColumnLayoutCollection(int width, int height) {
9 | super(width, height);
10 | }
11 |
12 | private List items;
13 |
14 | public List getItems() {
15 | return items;
16 | }
17 |
18 | public void setItems(List items) {
19 | this.items = items;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/waterfallayout/src/main/java/com/msisuzney/tv/waterfallayout/model/HorizontalLayoutItem.java:
--------------------------------------------------------------------------------
1 | package com.msisuzney.tv.waterfallayout.model;
2 | /**
3 | * @author: chenxin
4 | * @date: 2019-12-20
5 | * @email: chenxin7930@qq.com
6 | */
7 | public final class HorizontalLayoutItem extends Item {
8 | //宽高
9 | private int width, height;
10 |
11 | public int getWidth() {
12 | return width;
13 | }
14 |
15 | public void setWidth(int width) {
16 | this.width = width;
17 | }
18 |
19 | public int getHeight() {
20 | return height;
21 | }
22 |
23 | public void setHeight(int height) {
24 | this.height = height;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/waterfallayout/src/main/java/com/msisuzney/tv/waterfallayout/model/HorizontalLayoutCollection.java:
--------------------------------------------------------------------------------
1 | package com.msisuzney.tv.waterfallayout.model;
2 |
3 | import java.util.List;
4 | /**
5 | * @author: chenxin
6 | * @date: 2019-12-20
7 | * @email: chenxin7930@qq.com
8 | */
9 | public final class HorizontalLayoutCollection extends Collection {
10 |
11 | public HorizontalLayoutCollection(int width, int height) {
12 | super(width, height);
13 | }
14 |
15 | private List items;
16 |
17 | public List getItems() {
18 | return items;
19 | }
20 |
21 | public void setItems(List items) {
22 | this.items = items;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/waterfallayout/src/main/java/com/msisuzney/tv/waterfallayout/model/Collection.java:
--------------------------------------------------------------------------------
1 | package com.msisuzney.tv.waterfallayout.model;
2 | /**
3 | * @author: chenxin
4 | * @date: 2019-12-20
5 | * @email: chenxin7930@qq.com
6 | */
7 | public abstract class Collection {
8 | //行的宽高
9 | private int width, height;
10 |
11 | public Collection(int width, int height) {
12 | this.width = width;
13 | this.height = height;
14 | }
15 |
16 | public int getWidth() {
17 | return width;
18 | }
19 |
20 | public void setWidth(int width) {
21 | this.width = width;
22 | }
23 |
24 | public int getHeight() {
25 | return height;
26 | }
27 |
28 | public void setHeight(int height) {
29 | this.height = height;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/demo/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | android.enableJetifier=true
10 | android.useAndroidX=true
11 | org.gradle.jvmargs=-Xmx1536m
12 | # When configured, Gradle will run in incubating parallel mode.
13 | # This option should only be used with decoupled projects. More details, visit
14 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
15 | # org.gradle.parallel=true
16 |
17 |
18 |
--------------------------------------------------------------------------------
/waterfallayout/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/waterfallayout/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 28
5 |
6 |
7 | defaultConfig {
8 | minSdkVersion 14
9 | targetSdkVersion 28
10 | versionCode 1
11 | versionName "1.0"
12 | compileOptions {
13 | sourceCompatibility JavaVersion.VERSION_1_8
14 | targetCompatibility JavaVersion.VERSION_1_8
15 | }
16 | }
17 |
18 | buildTypes {
19 | release {
20 | minifyEnabled false
21 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
22 | }
23 | }
24 | lintOptions {
25 | abortOnError false
26 | }
27 |
28 | }
29 |
30 | dependencies {
31 | implementation 'androidx.recyclerview:recyclerview:1.0.0'
32 | implementation 'androidx.legacy:legacy-support-v4:1.0.0'
33 | }
34 |
--------------------------------------------------------------------------------
/demo/src/main/java/com/msisuzney/tv/demo/viewfactory/presenter/FooterViewPresenter.java:
--------------------------------------------------------------------------------
1 | package com.msisuzney.tv.demo.viewfactory.presenter;
2 |
3 | import com.msisuzney.tv.demo.R;
4 | import com.msisuzney.tv.waterfallayout.leanback.Presenter;
5 | import android.view.LayoutInflater;
6 | import android.view.ViewGroup;
7 |
8 | /**
9 | * @author: chenxin
10 | * @date: 2019-12-20
11 | * @email: chenxin7930@qq.com
12 | */
13 | public class FooterViewPresenter extends Presenter {
14 | @Override
15 | public ViewHolder onCreateViewHolder(ViewGroup parent) {
16 | return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.footer_view, parent, false));
17 | }
18 |
19 | @Override
20 | public void onBindViewHolder(ViewHolder viewHolder, Object item) {
21 |
22 | }
23 |
24 | @Override
25 | public void onUnbindViewHolder(ViewHolder viewHolder) {
26 |
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/demo/src/main/java/com/msisuzney/tv/demo/viewfactory/presenter/ColumnFocusChangeListenerTextViewPresenter.java:
--------------------------------------------------------------------------------
1 | package com.msisuzney.tv.demo.viewfactory.presenter;
2 |
3 | import android.view.LayoutInflater;
4 | import android.view.ViewGroup;
5 |
6 | import com.msisuzney.tv.demo.R;
7 | import com.msisuzney.tv.waterfallayout.leanback.Presenter;
8 |
9 | /**
10 | * @author: chenxin
11 | * @date: 2020-02-29
12 | * @email: chenxin7930@qq.com
13 | */
14 | public class ColumnFocusChangeListenerTextViewPresenter extends Presenter {
15 | @Override
16 | public ViewHolder onCreateViewHolder(ViewGroup parent) {
17 | return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.block_focus_state_view, parent, false));
18 | }
19 |
20 | @Override
21 | public void onBindViewHolder(ViewHolder viewHolder, Object item) {
22 |
23 | }
24 |
25 | @Override
26 | public void onUnbindViewHolder(ViewHolder viewHolder) {
27 |
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/waterfallayout/src/main/java/com/msisuzney/tv/waterfallayout/model/ColumnLayoutItem.java:
--------------------------------------------------------------------------------
1 | package com.msisuzney.tv.waterfallayout.model;
2 | /**
3 | * @author: chenxin
4 | * @date: 2019-12-20
5 | * @email: chenxin7930@qq.com
6 | */
7 | public final class ColumnLayoutItem extends Item {
8 |
9 | //位置和宽高
10 | private int x, y, width, height;
11 |
12 | public int getX() {
13 | return x;
14 | }
15 |
16 | public void setX(int x) {
17 | this.x = x;
18 | }
19 |
20 | public int getY() {
21 | return y;
22 | }
23 |
24 | public void setY(int y) {
25 | this.y = y;
26 | }
27 |
28 | public int getWidth() {
29 | return width;
30 | }
31 |
32 | public void setWidth(int width) {
33 | this.width = width;
34 | }
35 |
36 | public int getHeight() {
37 | return height;
38 | }
39 |
40 | public void setHeight(int height) {
41 | this.height = height;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/waterfallayout/src/main/java/com/msisuzney/tv/waterfallayout/leanback/ViewHolderTask.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2015 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | */
14 | package com.msisuzney.tv.waterfallayout.leanback;
15 |
16 | import androidx.recyclerview.widget.RecyclerView;
17 |
18 | /**
19 | * Interface for schedule task on a ViewHolder.
20 | */
21 | public interface ViewHolderTask {
22 | public void run(RecyclerView.ViewHolder viewHolder);
23 | }
--------------------------------------------------------------------------------
/demo/src/main/res/layout/block_image.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
19 |
20 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 chenxin
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/demo/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/demo/src/main/res/layout/footer_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
15 |
16 |
23 |
24 |
31 |
--------------------------------------------------------------------------------
/demo/src/main/res/layout/block_loading_state.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
14 |
15 |
22 |
23 |
30 |
--------------------------------------------------------------------------------
/waterfallayout/src/main/java/com/msisuzney/tv/waterfallayout/StateChangedObserver.java:
--------------------------------------------------------------------------------
1 | package com.msisuzney.tv.waterfallayout;
2 |
3 |
4 | import androidx.recyclerview.widget.RecyclerView;
5 | /**
6 | * @author: chenxin
7 | * @date: 2019-12-20
8 | * @email: chenxin7930@qq.com
9 | */
10 | /**
11 | * 状态变化监听类
12 | */
13 | public interface StateChangedObserver {
14 |
15 | /**
16 | * fragment is being paused
17 | */
18 | default void onFragmentPause() {
19 | }
20 |
21 | /**
22 | * Visibility of the fragment is changed
23 | *
24 | * @param isVisible
25 | */
26 | default void onFragmentVisibilityChanged(boolean isVisible) {
27 | }
28 |
29 | /**
30 | * Callback method to be invoked when RecyclerView's scroll state changes.
31 | *
32 | * @param recyclerView The RecyclerView whose scroll state has changed.
33 | * // * @param newState The updated scroll state. One of {SCROLL_STATE_IDLE},
34 | * // * { SCROLL_STATE_DRAGGING} or {SCROLL_STATE_SETTLING}.
35 | */
36 | default void onScrollStateChanged(RecyclerView recyclerView, int newState) {
37 | }
38 |
39 | }
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/waterfallayout/src/main/java/com/msisuzney/tv/waterfallayout/leanback/PresenterSelector.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | */
14 | package com.msisuzney.tv.waterfallayout.leanback;
15 |
16 | /**
17 | * A PresenterSelector is used to obtain a {@link Presenter} for a given Object.
18 | * Similar to {@link Presenter}, PresenterSelector is stateless.
19 | */
20 | public abstract class PresenterSelector {
21 | /**
22 | * Returns a presenter for the given item.
23 | */
24 | public abstract Presenter getPresenter(Object item);
25 |
26 | /**
27 | * Returns an array of all possible presenters. The returned array should
28 | * not be modified.
29 | */
30 | public Presenter[] getPresenters() {
31 | return null;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/waterfallayout/src/main/java/com/msisuzney/tv/waterfallayout/leanback/FacetProvider.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2015 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | */
14 | package com.msisuzney.tv.waterfallayout.leanback;
15 |
16 | /**
17 | * This is the query interface to supply optional features(aka facets) on an object without the need
18 | * of letting the object to subclass or implement java interfaces.
19 | */
20 | public interface FacetProvider {
21 |
22 | /**
23 | * Queries optional implemented facet.
24 | * @param facetClass Facet classes to query, examples are: class of
25 | * {@link ItemAlignmentFacet}.
26 | * @return Facet implementation for the facetClass or null if feature not implemented.
27 | */
28 | public Object getFacet(Class> facetClass);
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/demo/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 28
5 | defaultConfig {
6 | applicationId "com.msisuzney.tv.demo"
7 | minSdkVersion 15
8 | targetSdkVersion 28
9 | versionCode 1
10 | versionName "1.0"
11 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
12 | compileOptions {
13 | sourceCompatibility JavaVersion.VERSION_1_8
14 | targetCompatibility JavaVersion.VERSION_1_8
15 | }
16 | }
17 | buildTypes {
18 | release {
19 | minifyEnabled false
20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
21 | }
22 | }
23 | }
24 |
25 | dependencies {
26 | implementation fileTree(dir: 'libs', include: ['*.jar'])
27 | implementation 'androidx.appcompat:appcompat:1.0.0'
28 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
29 | implementation 'com.github.bumptech.glide:glide:4.10.0'
30 | implementation 'androidx.legacy:legacy-support-v4:1.0.0'
31 | implementation("com.squareup.okhttp3:okhttp:4.2.2")
32 | annotationProcessor 'com.github.bumptech.glide:compiler:4.10.0'
33 | implementation 'androidx.recyclerview:recyclerview:1.0.0'
34 | implementation 'com.google.code.gson:gson:2.8.5'
35 | implementation 'com.github.msisuzney:tv-waterfall-layout:1.0.1'
36 | // implementation project(':waterfallayout')
37 | }
38 |
--------------------------------------------------------------------------------
/demo/src/main/java/com/msisuzney/tv/demo/view/StateTextView.java:
--------------------------------------------------------------------------------
1 | package com.msisuzney.tv.demo.view;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.widget.TextView;
6 |
7 | import androidx.appcompat.widget.AppCompatTextView;
8 | import androidx.recyclerview.widget.RecyclerView;
9 |
10 | import com.msisuzney.tv.demo.MyStateChangedObserver;
11 |
12 | /**
13 | * @author: chenxin
14 | * @date: 2019-12-21
15 | * @email: chenxin7930@qq.com
16 | */
17 | public class StateTextView extends AppCompatTextView implements MyStateChangedObserver {
18 | public StateTextView(Context context) {
19 | super(context);
20 | }
21 |
22 | public StateTextView(Context context, AttributeSet attrs) {
23 | super(context, attrs);
24 | }
25 |
26 | public StateTextView(Context context, AttributeSet attrs, int defStyleAttr) {
27 | super(context, attrs, defStyleAttr);
28 | }
29 |
30 | @Override
31 | public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
32 | if (newState == RecyclerView.SCROLL_STATE_IDLE) {
33 | setText("RecyclerView STATE == SCROLL_STATE_IDLE");
34 | } else if (newState == RecyclerView.SCROLL_STATE_SETTLING) {
35 | setText("RecyclerView STATE == SCROLL_STATE_SETTLING");
36 | } else if (newState == RecyclerView.SCROLL_STATE_DRAGGING) {
37 | setText("RecyclerView STATE == SCROLL_STATE_DRAGGING");
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/waterfallayout/src/main/java/com/msisuzney/tv/waterfallayout/leanback/FacetProviderAdapter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2015 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | */
14 | package com.msisuzney.tv.waterfallayout.leanback;
15 |
16 | import androidx.recyclerview.widget.RecyclerView;
17 |
18 | /**
19 | * Optional interface that implemented by {@link RecyclerView.Adapter} to
20 | * query {@link FacetProvider} for a given type within Adapter. Note that
21 | * {@link RecyclerView.ViewHolder} may also implement {@link FacetProvider} which
22 | * has a higher priority than the one returned from the FacetProviderAdapter.
23 | */
24 | public interface FacetProviderAdapter {
25 |
26 | /**
27 | * Queries {@link FacetProvider} for a given type within Adapter.
28 | * @param type type of the item.
29 | * @return Facet provider for the type.
30 | */
31 | public FacetProvider getFacetProvider(int type);
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/demo/src/main/java/com/msisuzney/tv/demo/viewfactory/presenter/TitlePresenter.java:
--------------------------------------------------------------------------------
1 | package com.msisuzney.tv.demo.viewfactory.presenter;
2 |
3 | import com.msisuzney.tv.demo.R;
4 | import com.msisuzney.tv.demo.bean.TitleBean;
5 | import com.msisuzney.tv.waterfallayout.leanback.Presenter;
6 |
7 | import android.view.LayoutInflater;
8 | import android.view.View;
9 | import android.view.ViewGroup;
10 | import android.widget.TextView;
11 |
12 |
13 | /**
14 | * @author: chenxin
15 | * @date: 2019-12-20
16 | * @email: chenxin7930@qq.com
17 | */
18 | public class TitlePresenter extends Presenter {
19 | @Override
20 | public MyViewHolder onCreateViewHolder(ViewGroup parent) {
21 | return new MyViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.title_view, parent, false));
22 | }
23 |
24 | @Override
25 | public void onBindViewHolder(ViewHolder viewHolder, Object item) {
26 | if (viewHolder instanceof MyViewHolder) {
27 | MyViewHolder vh = (MyViewHolder) viewHolder;
28 | TitleBean titleBean = (TitleBean) item;
29 | vh.titleTV.setText(titleBean.getTitle());
30 | }
31 | }
32 |
33 | @Override
34 | public void onUnbindViewHolder(ViewHolder viewHolder) {
35 |
36 | }
37 |
38 | public static class MyViewHolder extends ViewHolder {
39 | TextView titleTV;
40 |
41 | public MyViewHolder(View view) {
42 | super(view);
43 | titleTV = (TextView) view;
44 | }
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/waterfallayout/src/main/java/com/msisuzney/tv/waterfallayout/leanback/OnChildLaidOutListener.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2015 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | */
14 | package com.msisuzney.tv.waterfallayout.leanback;
15 |
16 | import android.view.View;
17 | import android.view.ViewGroup;
18 |
19 | /**
20 | * Interface for receiving notification when a child of this
21 | * ViewGroup has been laid out.
22 | */
23 | public interface OnChildLaidOutListener {
24 | /**
25 | * Callback method to be invoked when a child of this ViewGroup has been
26 | * added to the view hierarchy and has been laid out.
27 | *
28 | * @param parent The ViewGroup where the layout happened.
29 | * @param view The view within the ViewGroup that was laid out.
30 | * @param position The position of the view in the adapter.
31 | * @param id The id of the child.
32 | */
33 | void onChildLaidOut(ViewGroup parent, View view, int position, long id);
34 | }
35 |
--------------------------------------------------------------------------------
/waterfallayout/src/main/java/com/msisuzney/tv/waterfallayout/leanback/SinglePresenterSelector.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | */
14 | package com.msisuzney.tv.waterfallayout.leanback;
15 |
16 | /**
17 | * A {@link PresenterSelector} that always returns the same {@link Presenter}.
18 | * Useful for rows of items of the same type that are all rendered the same way.
19 | */
20 | public final class SinglePresenterSelector extends PresenterSelector {
21 |
22 | private final Presenter mPresenter;
23 |
24 | /**
25 | * @param presenter The Presenter to return for every item.
26 | */
27 | public SinglePresenterSelector(Presenter presenter) {
28 | mPresenter = presenter;
29 | }
30 |
31 | @Override
32 | public Presenter getPresenter(Object item) {
33 | return mPresenter;
34 | }
35 |
36 | @Override
37 | public Presenter[] getPresenters() {
38 | return new Presenter[]{mPresenter};
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/waterfallayout/src/main/java/com/msisuzney/tv/waterfallayout/leanback/OnChildSelectedListener.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | */
14 | package com.msisuzney.tv.waterfallayout.leanback;
15 |
16 | import android.view.View;
17 | import android.view.ViewGroup;
18 |
19 | /**
20 | * Interface for receiving notification when a child of this
21 | * ViewGroup has been selected.
22 | * @deprecated Use {@link OnChildViewHolderSelectedListener}
23 | */
24 | @Deprecated
25 | public interface OnChildSelectedListener {
26 | /**
27 | * Callback method to be invoked when a child of this ViewGroup has been
28 | * selected.
29 | *
30 | * @param parent The ViewGroup where the selection happened.
31 | * @param view The view within the ViewGroup that is selected, or null if no
32 | * view is selected.
33 | * @param position The position of the view in the adapter, or NO_POSITION
34 | * if no view is selected.
35 | * @param id The id of the child that is selected, or NO_ID if no view is
36 | * selected.
37 | */
38 | void onChildSelected(ViewGroup parent, View view, int position, long id);
39 | }
40 |
--------------------------------------------------------------------------------
/demo/src/main/java/com/msisuzney/tv/demo/viewfactory/presenter/StateTextViewPresenter.java:
--------------------------------------------------------------------------------
1 | package com.msisuzney.tv.demo.viewfactory.presenter;
2 |
3 | import android.view.LayoutInflater;
4 | import android.view.ViewGroup;
5 |
6 | import com.msisuzney.tv.demo.MyStateChangeObservable;
7 | import com.msisuzney.tv.demo.R;
8 | import com.msisuzney.tv.demo.bean.RecyclerViewStateBean;
9 | import com.msisuzney.tv.demo.view.StateTextView;
10 | import com.msisuzney.tv.waterfallayout.leanback.Presenter;
11 |
12 | /**
13 | * @author: chenxin
14 | * @date: 2019-12-21
15 | * @email: chenxin7930@qq.com
16 | */
17 | public class StateTextViewPresenter extends Presenter {
18 |
19 | private MyStateChangeObservable stateChangeObservable;
20 |
21 | public StateTextViewPresenter(MyStateChangeObservable stateChangeObservable) {
22 | this.stateChangeObservable = stateChangeObservable;
23 | }
24 |
25 | @Override
26 | public ViewHolder onCreateViewHolder(ViewGroup parent) {
27 | return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.block_state_text_view, parent, false));
28 | }
29 |
30 | @Override
31 | public void onBindViewHolder(ViewHolder viewHolder, Object item) {
32 | if (item instanceof RecyclerViewStateBean) {
33 | StateTextView stateTextView = (StateTextView) viewHolder.view;
34 | if (stateChangeObservable != null) {
35 | stateChangeObservable.registerObserver(stateTextView);
36 | }
37 | }
38 | }
39 |
40 | @Override
41 | public void onUnbindViewHolder(ViewHolder viewHolder) {
42 | StateTextView stateTextView = (StateTextView) viewHolder.view;
43 | if (stateChangeObservable != null) {
44 | stateChangeObservable.unregisterObserver(stateTextView);
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/waterfallayout/src/main/java/com/msisuzney/tv/waterfallayout/presenter/RowPresenterSelector.java:
--------------------------------------------------------------------------------
1 | package com.msisuzney.tv.waterfallayout.presenter;
2 |
3 | import com.msisuzney.tv.waterfallayout.leanback.Presenter;
4 | import com.msisuzney.tv.waterfallayout.leanback.PresenterSelector;
5 |
6 | import com.msisuzney.tv.waterfallayout.model.ColumnLayoutCollection;
7 | import com.msisuzney.tv.waterfallayout.model.HorizontalLayoutCollection;
8 | /**
9 | * @author: chenxin
10 | * @date: 2019-12-20
11 | * @email: chenxin7930@qq.com
12 | */
13 | public final class RowPresenterSelector extends PresenterSelector {
14 |
15 | private ColumnLayoutRowPresenter absLayoutColumnLayoutRowPresenter;
16 | private HorizontalLayoutRowPresenter horizontalLayoutRowPresenter;
17 | private PresenterSelector otherPresenterSelector;
18 |
19 | public RowPresenterSelector(PresenterSelector blockPresenterSelector) {
20 | absLayoutColumnLayoutRowPresenter = new ColumnLayoutRowPresenter(blockPresenterSelector);
21 | horizontalLayoutRowPresenter = new HorizontalLayoutRowPresenter(blockPresenterSelector);
22 | }
23 |
24 |
25 | /**
26 | * 瀑布流中不是行的选择器,比如加载更多的提示View
27 | *
28 | * @param otherPresenterSelector
29 | */
30 | public void setOtherPresenterSelector(PresenterSelector otherPresenterSelector) {
31 | this.otherPresenterSelector = otherPresenterSelector;
32 | }
33 |
34 |
35 | @Override
36 | public Presenter getPresenter(Object item) {
37 | if (item instanceof ColumnLayoutCollection) {
38 | return absLayoutColumnLayoutRowPresenter;
39 | } else if (item instanceof HorizontalLayoutCollection) {
40 | return horizontalLayoutRowPresenter;
41 | } else {
42 | return otherPresenterSelector.getPresenter(item);
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/demo/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/waterfallayout/src/main/java/com/msisuzney/tv/waterfallayout/view/ColumnFocusChangeListener.java:
--------------------------------------------------------------------------------
1 | package com.msisuzney.tv.waterfallayout.view;
2 |
3 | import android.view.View;
4 | import android.view.ViewParent;
5 |
6 | /**
7 | * 可以监听整个栏目焦点变化的接口
8 | *
9 | * @author: chenxin
10 | * @date: 2020-01-06
11 | */
12 | public interface ColumnFocusChangeListener {
13 |
14 | void onColumnGainFocus();
15 |
16 | void onColumnLoseFocus();
17 |
18 | /*
19 |
20 | ViewRootImpl.java
21 | @Override
22 | public ViewParent getParent() {
23 | return null;
24 | }
25 |
26 | */
27 | default void attachToColumnLayout(View me) {
28 | ViewParent parent = me.getParent();
29 | if (parent instanceof ColumnLayout) {
30 | ColumnLayout columnLayout = (ColumnLayout) parent;
31 | columnLayout.addColumnFocusChangeListener(this);
32 | } else {
33 | while (parent != null) {
34 | parent = parent.getParent();
35 | if (parent instanceof ColumnLayout) {
36 | break;
37 | }
38 | }
39 | if (parent != null) {
40 | ColumnLayout columnLayout = (ColumnLayout) parent;
41 | columnLayout.addColumnFocusChangeListener(this);
42 | }
43 | }
44 | }
45 |
46 | default void detachFromColumnLayout(View me) {
47 | ViewParent parent = me.getParent();
48 | if (parent instanceof ColumnLayout) {
49 | ColumnLayout columnLayout = (ColumnLayout) parent;
50 | columnLayout.removeColumnFocusChangeListener(this);
51 | } else {
52 | while (parent != null) {
53 | parent = parent.getParent();
54 | if (parent instanceof ColumnLayout) {
55 | break;
56 | }
57 | }
58 | if (parent != null) {
59 | ColumnLayout columnLayout = (ColumnLayout) parent;
60 | columnLayout.removeColumnFocusChangeListener(this);
61 | }
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/demo/src/main/java/com/msisuzney/tv/demo/view/ColumnFocusChangeListenerTextView.java:
--------------------------------------------------------------------------------
1 | package com.msisuzney.tv.demo.view;
2 |
3 | import android.content.Context;
4 | import android.graphics.Color;
5 | import android.text.TextUtils;
6 | import android.util.AttributeSet;
7 | import android.widget.TextView;
8 |
9 | import androidx.annotation.Nullable;
10 | import androidx.appcompat.widget.AppCompatTextView;
11 |
12 | import com.msisuzney.tv.waterfallayout.view.ColumnFocusChangeListener;
13 |
14 | /**
15 | * @author: chenxin
16 | * @date: 2020-02-29
17 | * @email: chenxin7930@qq.com
18 | */
19 | public class ColumnFocusChangeListenerTextView extends AppCompatTextView implements ColumnFocusChangeListener {
20 | public ColumnFocusChangeListenerTextView(Context context) {
21 | super(context);
22 | }
23 |
24 | public ColumnFocusChangeListenerTextView(Context context, @Nullable AttributeSet attrs) {
25 | super(context, attrs);
26 | }
27 |
28 | public ColumnFocusChangeListenerTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
29 | super(context, attrs, defStyleAttr);
30 | }
31 |
32 | @Override
33 | public void onColumnGainFocus() {
34 | setText("the column has gained focus");
35 | textViewMarquee(this, true);
36 | }
37 |
38 | @Override
39 | public void onColumnLoseFocus() {
40 | setText("the column lost focus");
41 | textViewMarquee(this, false);
42 | }
43 |
44 | @Override
45 | protected void onAttachedToWindow() {
46 | super.onAttachedToWindow();
47 | attachToColumnLayout(this);
48 | }
49 |
50 | @Override
51 | protected void onDetachedFromWindow() {
52 | super.onDetachedFromWindow();
53 | detachFromColumnLayout(this);
54 | }
55 |
56 | static void textViewMarquee(TextView view, boolean enable) {
57 | if (enable) {
58 | view.setMaxLines(1);
59 | view.setTextColor(Color.RED);
60 | view.setEllipsize(TextUtils.TruncateAt.MARQUEE);
61 | view.setMarqueeRepeatLimit(-1);
62 | view.setSelected(true);
63 | } else {
64 | view.setMaxLines(1);
65 | view.setEllipsize(TextUtils.TruncateAt.END);
66 | view.setTextColor(Color.BLACK);
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/demo/src/main/java/com/msisuzney/tv/demo/viewfactory/ColumnItemViewFactory.java:
--------------------------------------------------------------------------------
1 | package com.msisuzney.tv.demo.viewfactory;
2 |
3 | import com.msisuzney.tv.demo.MyStateChangeObservable;
4 | import com.msisuzney.tv.demo.bean.ColumnFocusStateBean;
5 | import com.msisuzney.tv.demo.bean.RecyclerViewStateBean;
6 | import com.msisuzney.tv.demo.bean.TabBean;
7 | import com.msisuzney.tv.demo.viewfactory.presenter.ColumnFocusChangeListenerTextViewPresenter;
8 | import com.msisuzney.tv.demo.viewfactory.presenter.ImageViewPresenter;
9 | import com.msisuzney.tv.demo.viewfactory.presenter.ImageViewPresenter2;
10 | import com.msisuzney.tv.demo.viewfactory.presenter.StateTextViewPresenter;
11 | import com.msisuzney.tv.waterfallayout.leanback.Presenter;
12 | import com.msisuzney.tv.waterfallayout.leanback.PresenterSelector;
13 |
14 | import com.msisuzney.tv.waterfallayout.OnItemKeyListener;
15 |
16 | /**
17 | * @author: chenxin
18 | * @date: 2019-12-20
19 | * @email: chenxin7930@qq.com
20 | */
21 |
22 | /**
23 | * 行中的运营位的选择器
24 | */
25 | public class ColumnItemViewFactory extends PresenterSelector {
26 |
27 | private ImageViewPresenter imageViewPresenter;
28 | private ImageViewPresenter2 imageViewPresenter2;
29 | private StateTextViewPresenter stateTextViewPresenter;
30 |
31 | private ColumnFocusChangeListenerTextViewPresenter changeListenerTextViewPresenter;
32 |
33 | public ColumnItemViewFactory(MyStateChangeObservable observable,
34 | OnItemKeyListener onItemKeyListener) {
35 | imageViewPresenter = new ImageViewPresenter(onItemKeyListener);
36 | imageViewPresenter2 = new ImageViewPresenter2(onItemKeyListener);
37 | stateTextViewPresenter = new StateTextViewPresenter(observable);
38 | changeListenerTextViewPresenter = new ColumnFocusChangeListenerTextViewPresenter();
39 | }
40 |
41 | @Override
42 | public Presenter getPresenter(Object item) {
43 | if (item instanceof TabBean.ResultBean.AbsLayoutListBean) {
44 | return imageViewPresenter;
45 | } else if (item instanceof TabBean.ResultBean.HorizontalLayoutListBean) {
46 | return imageViewPresenter2;
47 | } else if (item instanceof RecyclerViewStateBean) {
48 | return stateTextViewPresenter;
49 | } else if (item instanceof ColumnFocusStateBean) {
50 | return changeListenerTextViewPresenter;
51 | }
52 | return null;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/waterfallayout/src/main/java/com/msisuzney/tv/waterfallayout/StateChangeObservable.java:
--------------------------------------------------------------------------------
1 | package com.msisuzney.tv.waterfallayout;
2 |
3 | import androidx.recyclerview.widget.RecyclerView;
4 |
5 | import java.util.ArrayList;
6 | /**
7 | * @author: chenxin
8 | * @date: 2019-12-20
9 | * @email: chenxin7930@qq.com
10 | */
11 |
12 | /**
13 | * 状态类,这里只定义了三种基础的状态,可以继承该类定义更多的状态
14 | *
15 | * @param
16 | */
17 | public abstract class StateChangeObservable {
18 |
19 | void notifyFragmentPause() {
20 | for (int i = mObservers.size() - 1; i >= 0; i--) {
21 | mObservers.get(i).onFragmentPause();
22 | }
23 | }
24 |
25 | void notifyFragmentVisibilityChanged(boolean isVisible) {
26 | for (int i = mObservers.size() - 1; i >= 0; i--) {
27 | mObservers.get(i).onFragmentVisibilityChanged(isVisible);
28 | }
29 | }
30 |
31 | void onScrollStateChanged(RecyclerView recyclerView, int newState) {
32 | for (int i = mObservers.size() - 1; i >= 0; i--) {
33 | mObservers.get(i).onScrollStateChanged(recyclerView, newState);
34 | }
35 | }
36 |
37 | /**
38 | * The list of observers. An observer can be in the list at most
39 | * once and will never be null.
40 | */
41 | protected final ArrayList mObservers = new ArrayList();
42 |
43 |
44 | public void registerObserver(T observer) {
45 | if (observer == null) {
46 | throw new IllegalArgumentException("The observer is null.");
47 | }
48 | synchronized (mObservers) {
49 | if (mObservers.contains(observer)) {
50 | throw new IllegalStateException("Observer " + observer + " is already registered.");
51 | }
52 | mObservers.add(observer);
53 | }
54 | }
55 |
56 | public void unregisterObserver(T observer) {
57 | if (observer == null) {
58 | throw new IllegalArgumentException("The observer is null.");
59 | }
60 | synchronized (mObservers) {
61 | int index = mObservers.indexOf(observer);
62 | if (index == -1) {
63 | throw new IllegalStateException("Observer " + observer + " was not registered.");
64 | }
65 | mObservers.remove(index);
66 | }
67 | }
68 |
69 | /**
70 | * Remove all registered observers.
71 | */
72 | public void unregisterAll() {
73 | synchronized (mObservers) {
74 | mObservers.clear();
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/waterfallayout/src/main/java/com/msisuzney/tv/waterfallayout/view/ColumnLayout.java:
--------------------------------------------------------------------------------
1 | package com.msisuzney.tv.waterfallayout.view;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.View;
6 | import android.view.ViewTreeObserver;
7 | import android.widget.AbsoluteLayout;
8 |
9 | import java.util.ArrayList;
10 |
11 | /**
12 | * @author: chenxin
13 | * @date: 2020-01-06
14 | */
15 | public class ColumnLayout extends AbsoluteLayout implements ViewTreeObserver.OnGlobalFocusChangeListener {
16 | public ColumnLayout(Context context) {
17 | super(context);
18 | }
19 |
20 | public ColumnLayout(Context context, AttributeSet attrs) {
21 | super(context, attrs);
22 | }
23 |
24 | public ColumnLayout(Context context, AttributeSet attrs, int defStyleAttr) {
25 | super(context, attrs, defStyleAttr);
26 | }
27 |
28 | @Override
29 | protected void onAttachedToWindow() {
30 | super.onAttachedToWindow();
31 | getRootView().getViewTreeObserver().addOnGlobalFocusChangeListener(this);
32 | }
33 |
34 | private ArrayList registeredFocusChangedViews = new ArrayList<>();
35 |
36 | @Override
37 | protected void onDetachedFromWindow() {
38 | super.onDetachedFromWindow();
39 | getRootView().getViewTreeObserver().removeOnGlobalFocusChangeListener(this);
40 | registeredFocusChangedViews.clear();
41 | }
42 |
43 | void addColumnFocusChangeListener(ColumnFocusChangeListener childView) {
44 | registeredFocusChangedViews.add(childView);
45 | }
46 |
47 | void removeColumnFocusChangeListener(ColumnFocusChangeListener childView) {
48 | registeredFocusChangedViews.remove(childView);
49 | }
50 |
51 | private boolean hasColumnFocus = false;
52 |
53 | @Override
54 | public void onGlobalFocusChanged(View oldFocus, View newFocus) {
55 | if (registeredFocusChangedViews.size() == 0) return;
56 | if (hasFocus()) {
57 | if (!hasColumnFocus) {
58 | hasColumnFocus = true;
59 | for (ColumnFocusChangeListener view : registeredFocusChangedViews) {
60 | view.onColumnGainFocus();
61 | }
62 | }
63 | } else {
64 | if (hasColumnFocus) {
65 | hasColumnFocus = false;
66 | for (ColumnFocusChangeListener view : registeredFocusChangedViews) {
67 | view.onColumnLoseFocus();
68 | }
69 | }
70 |
71 | }
72 | }
73 |
74 | }
75 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/waterfallayout/src/main/java/com/msisuzney/tv/waterfallayout/leanback/ItemAlignment.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | */
14 |
15 | package com.msisuzney.tv.waterfallayout.leanback;
16 |
17 | import static androidx.recyclerview.widget.RecyclerView.HORIZONTAL;
18 | import static androidx.recyclerview.widget.RecyclerView.VERTICAL;
19 |
20 | import android.view.View;
21 |
22 | /**
23 | * Defines alignment position on two directions of an item view. Typically item
24 | * view alignment is at the center of the view. The class allows defining
25 | * alignment at left/right or fixed offset/percentage position; it also allows
26 | * using descendant view by id match.
27 | */
28 | class ItemAlignment {
29 |
30 | final static class Axis extends ItemAlignmentFacet.ItemAlignmentDef {
31 | private int mOrientation;
32 |
33 | Axis(int orientation) {
34 | mOrientation = orientation;
35 | }
36 |
37 | /**
38 | * get alignment position relative to optical left/top of itemView.
39 | */
40 | public int getAlignmentPosition(View itemView) {
41 | return ItemAlignmentFacetHelper.getAlignmentPosition(itemView, this, mOrientation);
42 | }
43 | }
44 |
45 | private int mOrientation = HORIZONTAL;
46 |
47 | final public Axis vertical = new Axis(VERTICAL);
48 |
49 | final public Axis horizontal = new Axis(HORIZONTAL);
50 |
51 | private Axis mMainAxis = horizontal;
52 |
53 | private Axis mSecondAxis = vertical;
54 |
55 | final public Axis mainAxis() {
56 | return mMainAxis;
57 | }
58 |
59 | final public Axis secondAxis() {
60 | return mSecondAxis;
61 | }
62 |
63 | final public void setOrientation(int orientation) {
64 | mOrientation = orientation;
65 | if (mOrientation == HORIZONTAL) {
66 | mMainAxis = horizontal;
67 | mSecondAxis = vertical;
68 | } else {
69 | mMainAxis = vertical;
70 | mSecondAxis = horizontal;
71 | }
72 | }
73 |
74 | final public int getOrientation() {
75 | return mOrientation;
76 | }
77 |
78 |
79 | }
80 |
--------------------------------------------------------------------------------
/waterfallayout/src/main/java/com/msisuzney/tv/waterfallayout/leanback/OnChildViewHolderSelectedListener.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | */
14 | package com.msisuzney.tv.waterfallayout.leanback;
15 |
16 | import com.msisuzney.tv.waterfallayout.leanback.ItemAlignmentFacet.ItemAlignmentDef;
17 | import androidx.recyclerview.widget.RecyclerView;
18 |
19 | /**
20 | * Interface for receiving notification when a child of this ViewGroup has been selected.
21 | * There are two methods:
22 | *
23 | * {link {@link #onChildViewHolderSelected(RecyclerView, RecyclerView.ViewHolder, int, int)}}
24 | * is called when the view holder is about to be selected. The listener could change size
25 | * of the view holder in this callback.
26 | *
27 | *
28 | * {link {@link #onChildViewHolderSelectedAndPositioned(RecyclerView, RecyclerView.ViewHolder,
29 | * int, int)} is called when view holder has been selected and laid out in RecyclerView.
30 | *
31 | *
32 | */
33 | public abstract class OnChildViewHolderSelectedListener {
34 | /**
35 | * Callback method to be invoked when a child of this ViewGroup has been selected. Listener
36 | * might change the size of the child and the position of the child is not finalized. To get
37 | * the final layout position of child, overide {@link #onChildViewHolderSelectedAndPositioned(
38 | * RecyclerView, RecyclerView.ViewHolder, int, int)}.
39 | *
40 | * @param parent The RecyclerView where the selection happened.
41 | * @param child The ViewHolder within the RecyclerView that is selected, or null if no
42 | * view is selected.
43 | * @param position The position of the view in the adapter, or NO_POSITION
44 | * if no view is selected.
45 | * @param subposition The index of which {@link ItemAlignmentDef} being used,
46 | * 0 if there is no ItemAlignmentDef defined for the item.
47 | */
48 | public void onChildViewHolderSelected(RecyclerView parent, RecyclerView.ViewHolder child,
49 | int position, int subposition) {
50 | }
51 |
52 | /**
53 | * Callback method to be invoked when a child of this ViewGroup has been selected and
54 | * positioned.
55 | *
56 | * @param parent The RecyclerView where the selection happened.
57 | * @param child The ViewHolder within the RecyclerView that is selected, or null if no
58 | * view is selected.
59 | * @param position The position of the view in the adapter, or NO_POSITION
60 | * if no view is selected.
61 | * @param subposition The index of which {@link ItemAlignmentDef} being used,
62 | * 0 if there is no ItemAlignmentDef defined for the item.
63 | */
64 | public void onChildViewHolderSelectedAndPositioned(RecyclerView parent,
65 | RecyclerView.ViewHolder child, int position, int subposition) {
66 | }
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/waterfallayout/src/main/java/com/msisuzney/tv/waterfallayout/presenter/ColumnLayoutRowPresenter.java:
--------------------------------------------------------------------------------
1 | package com.msisuzney.tv.waterfallayout.presenter;
2 |
3 | import com.msisuzney.tv.waterfallayout.R;
4 | import com.msisuzney.tv.waterfallayout.leanback.Presenter;
5 | import com.msisuzney.tv.waterfallayout.leanback.PresenterSelector;
6 | import com.msisuzney.tv.waterfallayout.model.ColumnLayoutCollection;
7 | import com.msisuzney.tv.waterfallayout.model.ColumnLayoutItem;
8 | import com.msisuzney.tv.waterfallayout.view.ColumnLayout;
9 |
10 | import android.view.View;
11 | import android.view.ViewGroup;
12 | import android.widget.AbsoluteLayout;
13 |
14 | import java.util.List;
15 |
16 |
17 | class ColumnLayoutRowPresenter extends Presenter {
18 | private PresenterSelector blockPresenterSelector;
19 |
20 |
21 | ColumnLayoutRowPresenter(PresenterSelector blockPresenterSelector) {
22 | this.blockPresenterSelector = blockPresenterSelector;
23 | }
24 |
25 | @Override
26 | public ViewHolder onCreateViewHolder(ViewGroup parent) {
27 | View view = new ColumnLayout(parent.getContext());
28 | return new ColumnViewHolder(view);
29 | }
30 |
31 |
32 | @Override
33 | public void onBindViewHolder(ViewHolder viewHolder, Object item) {
34 | ColumnLayoutCollection collection = (ColumnLayoutCollection) item;
35 | ColumnViewHolder columnViewHolder = (ColumnViewHolder) viewHolder;
36 | AbsoluteLayout parentView = columnViewHolder.getAbsoluteLayout();
37 | ViewGroup.MarginLayoutParams lp = new ViewGroup.MarginLayoutParams(collection.getWidth(), collection.getHeight());
38 | parentView.setLayoutParams(lp);
39 |
40 |
41 | List items = collection.getItems();
42 | if (items != null) {
43 | for (ColumnLayoutItem layoutItem : items) {
44 | Presenter presenter = blockPresenterSelector.getPresenter(layoutItem.getData());
45 | if (presenter != null) {
46 | ViewHolder vh = presenter.onCreateViewHolder(parentView);
47 | vh.view.setTag(R.id.lb_view_data_tag, layoutItem.getData());
48 | vh.view.setTag(R.id.lb_view_holder_tag, vh);
49 | presenter.onBindViewHolder(vh, layoutItem.getData());
50 | AbsoluteLayout.LayoutParams layoutParams = new AbsoluteLayout.LayoutParams(layoutItem.getWidth(),
51 | layoutItem.getHeight(), layoutItem.getX(), layoutItem.getY());
52 | parentView.addView(vh.view, layoutParams);
53 | }
54 | }
55 | }
56 | }
57 |
58 | //被RV回收时调用
59 | @Override
60 | public void onUnbindViewHolder(ViewHolder viewHolder) {
61 | ColumnViewHolder columnViewHolder = (ColumnViewHolder) viewHolder;
62 | AbsoluteLayout parentView = columnViewHolder.getAbsoluteLayout();
63 | for (int i = 0; i < parentView.getChildCount(); i++) {
64 | View view = parentView.getChildAt(i);
65 | Object data = view.getTag(R.id.lb_view_data_tag);
66 | ViewHolder vh = (ViewHolder) view.getTag(R.id.lb_view_holder_tag);
67 | Presenter presenter = blockPresenterSelector.getPresenter(data);
68 | presenter.onUnbindViewHolder(vh);
69 | view.setTag(R.id.lb_view_holder_tag, null);
70 | view.setTag(R.id.lb_view_data_tag, null);
71 | }
72 | parentView.removeAllViews();
73 | }
74 |
75 |
76 | public final class ColumnViewHolder extends ViewHolder {
77 |
78 | private AbsoluteLayout absoluteLayout;
79 |
80 | public ColumnViewHolder(View view) {
81 | super(view);
82 | absoluteLayout = (AbsoluteLayout) view;
83 | }
84 |
85 | public AbsoluteLayout getAbsoluteLayout() {
86 | return absoluteLayout;
87 | }
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/waterfallayout/src/main/java/com/msisuzney/tv/waterfallayout/leanback/VerticalGridView.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | */
14 | package com.msisuzney.tv.waterfallayout.leanback;
15 |
16 | import android.content.Context;
17 | import android.content.res.TypedArray;
18 | import com.msisuzney.tv.waterfallayout.R;
19 | import androidx.recyclerview.widget.RecyclerView;
20 | import android.util.AttributeSet;
21 | import android.util.TypedValue;
22 |
23 | /**
24 | * A {@link android.view.ViewGroup} that shows items in a vertically scrolling list. The items
25 | * come from the {@link RecyclerView.Adapter} associated with this view.
26 | *
27 | * {@link RecyclerView.Adapter} can optionally implement {@link FacetProviderAdapter} which
28 | * provides {@link FacetProvider} for a given view type; {@link RecyclerView.ViewHolder}
29 | * can also implement {@link FacetProvider}. Facet from ViewHolder
30 | * has a higher priority than the one from FacetProviderAdapter associated with viewType.
31 | * Supported optional facets are:
32 | *
33 | *
{@link ItemAlignmentFacet}
34 | * When this facet is provided by ViewHolder or FacetProviderAdapter, it will
35 | * override the item alignment settings set on VerticalGridView. This facet also allows multiple
36 | * alignment positions within one ViewHolder.
37 | *
38 | *
39 | */
40 | public class VerticalGridView extends BaseGridView {
41 |
42 | public VerticalGridView(Context context) {
43 | this(context, null);
44 | }
45 |
46 | public VerticalGridView(Context context, AttributeSet attrs) {
47 | this(context, attrs, 0);
48 | }
49 |
50 | public VerticalGridView(Context context, AttributeSet attrs, int defStyle) {
51 | super(context, attrs, defStyle);
52 | mLayoutManager.setOrientation(RecyclerView.VERTICAL);
53 | initAttributes(context, attrs);
54 | }
55 |
56 | protected void initAttributes(Context context, AttributeSet attrs) {
57 | initBaseGridViewAttributes(context, attrs);
58 | TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.lbVerticalGridView);
59 | setColumnWidth(a);
60 | setNumColumns(a.getInt(R.styleable.lbVerticalGridView_numberOfColumns, 1));
61 | a.recycle();
62 | }
63 |
64 | void setColumnWidth(TypedArray array) {
65 | TypedValue typedValue = array.peekValue(R.styleable.lbVerticalGridView_columnWidth);
66 | if (typedValue != null) {
67 | int size = array.getLayoutDimension(R.styleable.lbVerticalGridView_columnWidth, 0);
68 | setColumnWidth(size);
69 | }
70 | }
71 |
72 | /**
73 | * Sets the number of columns. Defaults to one.
74 | */
75 | public void setNumColumns(int numColumns) {
76 | mLayoutManager.setNumRows(numColumns);
77 | requestLayout();
78 | }
79 |
80 | /**
81 | * Sets the column width.
82 | *
83 | * @param width May be {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}, or a size
84 | * in pixels. If zero, column width will be fixed based on number of columns
85 | * and view width.
86 | */
87 | public void setColumnWidth(int width) {
88 | mLayoutManager.setRowHeight(width);
89 | requestLayout();
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Android TV瀑布流控件
2 | **一种基于Android Leanback改造的Android TV瀑布流布局**
3 |
4 | 示例效果如下:
5 |
29 |
30 | Presenters根据不同的Bean创建不同的View,具体见[android/tv-samples](https://github.com/android/tv-samples)
31 |
32 | #### 2.使用方式
33 | 0. 添加依赖
34 | ```gradle
35 | //1) 根目录下build.gradle
36 | allprojects {
37 | repositories {
38 | //add jitpack.io repo
39 | maven { url 'https://jitpack.io' }
40 | }
41 | }
42 |
43 | //2) module build.gradle
44 | dependencies {
45 | //add lib
46 | implementation 'com.github.msisuzney:tv-waterfall-layout:1.0.1'
47 | }
48 | ```
49 | 1. 继承`RowsFragment`
50 | 2. 添加`ColumnLayout`布局栏目,使用`ColumnLayoutCollection`定义栏目的宽高,再使用`ColumnLayoutItem`定义子`View`的位置、大小、bean类型与数据,
51 | 最后使用`setItems`方法将`ColumnLayoutItems`添加到`ColumnLayoutCollection`中
52 | 3. 添加水平滑动的`HorizontalGirdView`布局栏目,使用`HorizontalLayoutCollection`定义栏目的宽高,再使用`HorizontalLayoutItem`定义子`View`的大小、bean类型与数据,
53 | 最后使用`setItems`方法将`HorizontalLayoutItems`添加到`HorizontalLayoutCollection`
54 | 4. 使用`RowsFragment`#`add`方法将`ColumnLayoutCollection`/`HorizontalLayoutCollection`添加到布局中
55 | 5. 复写`RowsFragment`#`initBlockPresenterSelector`方法,返回的`PresenterSelector`用于根据bean类型为栏目创建不同的`View`
56 |
57 | 详细使用见demo module代码,简易代码如下:
58 | ```java
59 | public class MyFragment extends RowsFragment {
60 |
61 |
62 | @Override
63 | protected PresenterSelector initBlockPresenterSelector() {
64 | //1.提供所有行中的运营位的Presenters,用于创建对应的View
65 | return new PresenterSelector() {
66 | @Override
67 | public Presenter getPresenter(Object item) {
68 | return new ImageViewPresenter(null);
69 | }
70 | };
71 | }
72 |
73 | @Override
74 | public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
75 | super.onViewCreated(view, savedInstanceState);
76 | //2. 构造水平滑动布局的Model
77 | HorizontalLayoutItem item = new HorizontalLayoutItem();
78 | item.setWidth(200);
79 | item.setHeight(200);
80 | List items = new ArrayList<>();
81 | items.add(item);
82 | HorizontalLayoutCollection horizontalLayoutCollection =
83 | new HorizontalLayoutCollection(ViewGroup.LayoutParams.MATCH_PARENT, 200);
84 | horizontalLayoutCollection.setItems(items);
85 |
86 | //3. 构造绝对布局的Model
87 | AbsoluteLayoutItem item1 = new AbsoluteLayoutItem();
88 | item1.setHeight(200);
89 | item1.setWidth(200);
90 | item1.setX(200);
91 | item1.setY(10);
92 | List items1 = new ArrayList<>();
93 | items1.add(item1);
94 | AbsoluteLayoutCollection absoluteLayoutCollection =
95 | new AbsoluteLayoutCollection(ViewGroup.LayoutParams.MATCH_PARENT, 400);
96 | absoluteLayoutCollection.setItems(items1);
97 |
98 | //4. 添加到布局中
99 | add(horizontalLayoutCollection);
100 | add(absoluteLayoutCollection);
101 | }
102 | }
103 |
104 |
105 | ```
106 |
107 |
--------------------------------------------------------------------------------
/waterfallayout/src/main/java/com/msisuzney/tv/waterfallayout/leanback/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | */
14 |
15 | /**
16 | *
Support classes providing low level Leanback user interface building blocks:
17 | * widgets and helpers.
18 | *
19 | * The core interface to the developer’s model is the
20 | * {@link com.msisuzney.tv.waterfallayout.leanback.ObjectAdapter}. It is similar to Adapter and the
21 | * RecyclerView Adapter, but separates iterating items from presenting them as Views.
22 | * Concrete implementations include
23 | * {@link com.msisuzney.tv.waterfallayout.leanback.ArrayObjectAdapter} and
24 | * {@link androidx.leanback.widget.CursorObjectAdapter}, but a developer is free to use a
25 | * subclass of an ObjectAdapter to iterate over any existing Object hierarchy.
26 | *
27 | *
28 | * A {@link com.msisuzney.tv.waterfallayout.leanback.Presenter} creates Views and binds data from an Object
29 | * to those Views. This is the
30 | * complementary piece to ObjectAdapter that corresponds to existing Android adapter classes.
31 | * The benefit to separating out a Presenter is that we can use it to generate Views outside of the
32 | * context of an adapter. For example, a UI may represent data from a single Object in several places
33 | * at once. Each View that needs to be generated can be produced by a different Presenter, while the
34 | * Object is retrieved from the ObjectAdapter once.
35 | *
36 | * A {@link com.msisuzney.tv.waterfallayout.leanback.PresenterSelector} determines which Presenter to use
37 | * for a given Object from an
38 | * ObjectAdapter. Two common cases are when an ObjectAdapter uses the same View type for every element
39 | * ({@link com.msisuzney.tv.waterfallayout.leanback.SinglePresenterSelector}), and when the Presenter is
40 | * determined by the Java class of
41 | * the element ({@link androidx.leanback.widget.ClassPresenterSelector}). A developer is
42 | * able to implement any selection logic
43 | * as a PresenterSelector. For example, if all the elements of an ObjectAdapter have the same type,
44 | * but certain elements are to be rendered using a 'promotional content' view in the developer’s
45 | * application, the PresenterSelector may inspect the fields of each element before choosing the
46 | * appropriate Presenter.
47 | *
48 | *
49 | * The basic navigation model for Leanback is that of a vertical list of rows, each of which may
50 | * be a horizontal list of items. Therefore, Leanback uses ObjectAdapters both for defining the
51 | * horizontal data items as well as the list of rows themselves.
52 | *
53 | *
Leanback defines a few basic data model classes for rows: the
54 | * {@link androidx.leanback.widget.Row}, which defines the
55 | * abstract concept of a row with a header; and {@link androidx.leanback.widget.ListRow},
56 | * a concrete Row implementation that uses an ObjectAdapter to present a horizontal list of items.
57 | * The corresponding presenter for the ListRow is the
58 | * {@link androidx.leanback.widget.ListRowPresenter}.
59 | *
60 | *
61 | * Other types of Rows and corresponding RowPresenters are provided; however the application may
62 | * define a custom subclass of {@link androidx.leanback.widget.Row} and
63 | * {@link androidx.leanback.widget.RowPresenter}.
64 | *