├── .gitignore ├── Demo ├── .gitignore ├── androidtvwidget │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── com │ │ │ └── open │ │ │ └── androidtvwidget │ │ │ └── ApplicationTest.java │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── open │ │ │ │ └── androidtvwidget │ │ │ │ ├── adapter │ │ │ │ └── BaseTabTitleAdapter.java │ │ │ │ ├── bridge │ │ │ │ ├── BaseEffectBridge.java │ │ │ │ ├── BaseEffectBridgeWrapper.java │ │ │ │ ├── EffectNoDrawBridge.java │ │ │ │ ├── OpenEffectBridge.java │ │ │ │ └── RecyclerViewBridge.java │ │ │ │ ├── cache │ │ │ │ └── BitmapMemoryCache.java │ │ │ │ ├── keyboard │ │ │ │ ├── KeyRow.java │ │ │ │ ├── PopupSoftKey.java │ │ │ │ ├── SaveKeyEvent.java │ │ │ │ ├── SkbContainer.java │ │ │ │ ├── SkbPool.java │ │ │ │ ├── SoftKey.java │ │ │ │ ├── SoftKeyBoardListener.java │ │ │ │ ├── SoftKeyBoardable.java │ │ │ │ ├── SoftKeyboard.java │ │ │ │ ├── SoftKeyboardView.java │ │ │ │ └── XmlKeyboardLoader.java │ │ │ │ ├── leanback │ │ │ │ ├── adapter │ │ │ │ │ └── GeneralAdapter.java │ │ │ │ ├── mode │ │ │ │ │ ├── DefualtListPresenter.java │ │ │ │ │ ├── ItemHeaderPresenter.java │ │ │ │ │ ├── ItemListPresenter.java │ │ │ │ │ ├── ListRow.java │ │ │ │ │ ├── ListRowPresenter.java │ │ │ │ │ └── OpenPresenter.java │ │ │ │ ├── recycle │ │ │ │ │ ├── AutoMeaureGridLayoutManager.java │ │ │ │ │ ├── GridLayoutManagerTV.java │ │ │ │ │ ├── Helper │ │ │ │ │ │ └── ItemKeyHelper.java │ │ │ │ │ ├── LinearLayoutManagerTV.java │ │ │ │ │ ├── RecyclerViewTV.java │ │ │ │ │ └── impl │ │ │ │ │ │ └── PrvInterface.java │ │ │ │ └── widget │ │ │ │ │ ├── ItemContainerView.java │ │ │ │ │ ├── ListContentView.java │ │ │ │ │ └── NORLinearLayout.java │ │ │ │ ├── menu │ │ │ │ ├── MenuDataObservable.java │ │ │ │ ├── MenuSetObserver.java │ │ │ │ ├── OpenMenu.java │ │ │ │ ├── OpenMenuImpl.java │ │ │ │ ├── OpenMenuItem.java │ │ │ │ ├── OpenMenuItemImpl.java │ │ │ │ └── OpenMenuItemView.java │ │ │ │ ├── utils │ │ │ │ ├── AnimateUtils.java │ │ │ │ ├── CustomApplicationHelper.java │ │ │ │ ├── DisplayUtil.java │ │ │ │ ├── DrawUtils.java │ │ │ │ ├── GenerateViewId.java │ │ │ │ ├── IdentifierUtuils.java │ │ │ │ ├── NetWorkUtils.java │ │ │ │ ├── OPENLOG.java │ │ │ │ ├── ShellUtils.java │ │ │ │ └── Utils.java │ │ │ │ └── view │ │ │ │ ├── ExpendGridView.java │ │ │ │ ├── FrameMainLayout.java │ │ │ │ ├── GridViewTV.java │ │ │ │ ├── LabelView.java │ │ │ │ ├── LinearMainLayout.java │ │ │ │ ├── ListViewTV.java │ │ │ │ ├── MainUpView.java │ │ │ │ ├── OpenTabHost.java │ │ │ │ ├── ReflectItemView.java │ │ │ │ ├── RelativeMainLayout.java │ │ │ │ ├── SmoothHorizontalScrollView.java │ │ │ │ ├── SmoothVorizontalScrollView.java │ │ │ │ ├── TextViewWithTTF.java │ │ │ │ ├── ViewPagerTV.java │ │ │ │ └── WidgetTvViewBring.java │ │ └── res │ │ │ ├── drawable │ │ │ └── nocolor.png │ │ │ ├── layout │ │ │ ├── lb_content_view.xml │ │ │ ├── lb_item_container.xml │ │ │ ├── list_menu_item_icon.xml │ │ │ ├── list_menu_item_layout.xml │ │ │ ├── open_menu_view.xml │ │ │ ├── softkey_layout_view.xml │ │ │ └── tabhost_title_head.xml │ │ │ └── values │ │ │ ├── attrs.xml │ │ │ ├── dimens.xml │ │ │ └── strings.xml │ │ └── test │ │ └── java │ │ └── com │ │ └── open │ │ └── androidtvwidget │ │ └── ExampleUnitTest.java ├── app │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── com │ │ │ └── open │ │ │ └── demo │ │ │ └── ApplicationTest.java │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── open │ │ │ │ └── demo │ │ │ │ ├── DemoGridViewActivity.java │ │ │ │ ├── DemoKeyBoardActivity.java │ │ │ │ ├── DemoListViewActivity.java │ │ │ │ ├── DemoMenuActivity.java │ │ │ │ ├── DemoRecyclerviewActivity.java │ │ │ │ ├── DemoViewPagerActivity.java │ │ │ │ ├── DemoViewflipperActivity.java │ │ │ │ ├── MainActivity.java │ │ │ │ ├── MenuAnimationUtils.java │ │ │ │ ├── NewViewPager.java │ │ │ │ ├── Rotate3dAnimation.java │ │ │ │ ├── adapter │ │ │ │ ├── GridViewHolder.java │ │ │ │ ├── LeftMenuPresenter.java │ │ │ │ ├── OpenTabTitleAdapter.java │ │ │ │ └── RecyclerViewPresenter.java │ │ │ │ ├── menu │ │ │ │ └── TreeMenuPresenter.java │ │ │ │ └── mode │ │ │ │ ├── ItemHeaderPresenter.java │ │ │ │ ├── LeanbackTestData.java │ │ │ │ ├── Movie.java │ │ │ │ ├── OpenCardView.java │ │ │ │ └── TestMoviceListPresenter.java │ │ └── res │ │ │ ├── drawable-hdpi │ │ │ ├── video_cover_cursor.9.png │ │ │ └── white_light_10.9.png │ │ │ ├── drawable-xhdpi │ │ │ ├── cd_bg_1.png │ │ │ ├── cursor_shine.png │ │ │ ├── delete.png │ │ │ ├── dianying.png │ │ │ ├── dou_hao_e.png │ │ │ ├── g_search_icon.png │ │ │ ├── grid_view_item_test.jpg │ │ │ ├── ic_launcher.png │ │ │ ├── icon_v_default.jpg │ │ │ ├── ju_hao_e.png │ │ │ ├── key_move_left.png │ │ │ ├── key_move_right.png │ │ │ ├── kongge.png │ │ │ ├── main_bg.jpg │ │ │ ├── mainview_cloudlist.png │ │ │ ├── mainview_huodong.png │ │ │ ├── mainview_searchsong.png │ │ │ ├── mainview_tuijian.png │ │ │ ├── mainview_type.png │ │ │ ├── mine_account.png │ │ │ ├── mine_handler.png │ │ │ ├── quit_skb.png │ │ │ ├── radio_btn_check_false.png │ │ │ ├── radio_btn_check_true.png │ │ │ ├── softkey_bg_press2.png │ │ │ ├── softkey_bg_select2.9.png │ │ │ ├── t9_key0.png │ │ │ ├── t9_key1.png │ │ │ ├── t9_key2.png │ │ │ ├── t9_key3.png │ │ │ ├── t9_key4.png │ │ │ ├── t9_key5.png │ │ │ ├── t9_key6.png │ │ │ ├── t9_key7.png │ │ │ ├── t9_key8.png │ │ │ ├── t9_key9.png │ │ │ ├── t9_key_clear.png │ │ │ ├── top_test_bg.png │ │ │ ├── xiaoxie.png │ │ │ └── zongyi.png │ │ │ ├── drawable │ │ │ ├── ic_bg_menu.9.png │ │ │ ├── ic_sp_block_focus.9.png │ │ │ ├── item_shadow.9.png │ │ │ ├── key_bg_rectangle.xml │ │ │ ├── tab_title_background.xml │ │ │ └── test_rectangle.xml │ │ │ ├── layout │ │ │ ├── activity_viewflipper_demo.xml │ │ │ ├── demo_grid_view.xml │ │ │ ├── demo_json_load_activity.xml │ │ │ ├── demo_keyboard_activity.xml │ │ │ ├── demo_list_view.xml │ │ │ ├── demo_menu_activity.xml │ │ │ ├── demo_recyclerview_activity.xml │ │ │ ├── demo_viewpager_activity.xml │ │ │ ├── item_gridview.xml │ │ │ ├── item_header_view.xml │ │ │ ├── item_listview.xml │ │ │ ├── item_recyclerview_view.xml │ │ │ ├── json_item_layout_view.xml │ │ │ ├── lb_h_item.xml │ │ │ ├── tab_view_indicator_item.xml │ │ │ ├── test_main.xml │ │ │ ├── test_page1.xml │ │ │ ├── test_page2.xml │ │ │ ├── test_page3.xml │ │ │ └── test_page4.xml │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── values-1280x720 │ │ │ ├── h_dimens.xml │ │ │ └── w_dimens.xml │ │ │ ├── values-1280x800 │ │ │ ├── h_dimens.xml │ │ │ └── w_dimens.xml │ │ │ ├── values-1920x1080 │ │ │ ├── h_dimens.xml │ │ │ └── w_dimens.xml │ │ │ ├── values-2560x1440 │ │ │ ├── h_dimens.xml │ │ │ └── w_dimens.xml │ │ │ ├── values │ │ │ ├── color.xml │ │ │ ├── dimens.xml │ │ │ ├── h_dimens.xml │ │ │ ├── ids.xml │ │ │ ├── strings.xml │ │ │ ├── styles.xml │ │ │ └── w_dimens.xml │ │ │ └── xml │ │ │ ├── sbd_number.xml │ │ │ ├── sbd_qwerty.xml │ │ │ ├── skb_all_key.xml │ │ │ └── skb_t9_keys.xml │ │ └── test │ │ └── java │ │ └── com │ │ └── open │ │ └── demo │ │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties └── settings.gradle ├── LICENSE ├── README.md ├── Tool ├── GenerateValueFiles.java ├── res │ ├── values-1024x600 │ │ ├── h_dimens.xml │ │ └── w_dimens.xml │ ├── values-1280x672 │ │ ├── h_dimens.xml │ │ └── w_dimens.xml │ ├── values-1280x720 │ │ ├── h_dimens.xml │ │ └── w_dimens.xml │ ├── values-1280x736 │ │ ├── h_dimens.xml │ │ └── w_dimens.xml │ ├── values-1280x800 │ │ ├── h_dimens.xml │ │ └── w_dimens.xml │ ├── values-1366x768 │ │ ├── h_dimens.xml │ │ └── w_dimens.xml │ ├── values-1920x1080 │ │ ├── h_dimens.xml │ │ └── w_dimens.xml │ ├── values-2048x1440 │ │ ├── h_dimens.xml │ │ └── w_dimens.xml │ ├── values-2048x1536 │ │ ├── h_dimens.xml │ │ └── w_dimens.xml │ ├── values-2560x1440 │ │ ├── h_dimens.xml │ │ └── w_dimens.xml │ └── values-2560x1600 │ │ ├── h_dimens.xml │ │ └── w_dimens.xml └── 使用说明.txt └── doc ├── demo.gif ├── doc.zip ├── keyboard_move.gif ├── keyboard_nomove.gif └── menu.gif /.gitignore: -------------------------------------------------------------------------------- 1 | # built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # files for the dex VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # generated files 12 | bin/ 13 | gen/ 14 | 15 | # Local configuration file (sdk path, etc) 16 | local.properties 17 | 18 | # Eclipse project files 19 | .classpath 20 | .project 21 | 22 | # Proguard folder generated by Eclipse 23 | proguard/ 24 | 25 | # Intellij project files 26 | *.iml 27 | *.ipr 28 | *.iws 29 | .idea/ 30 | 31 | -------------------------------------------------------------------------------- /Demo/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'com.jfrog.bintray' 3 | apply plugin: 'com.github.dcendents.android-maven' 4 | 5 | Properties properties = new Properties() 6 | properties.load(project.rootProject.file('local.properties').newDataInputStream()) 7 | 8 | def projectName = "ismartv_androidtvwidget" 9 | def versionTag = rootProject.ext.moduleVersion 10 | def groupId = "com.open.androidtvwidget" 11 | def siteUrl = 'git@github.com:JasonFengIce/videocache.git' 12 | def gitUrl = 'git@github.com:JasonFengIce/videocache.gitt' 13 | 14 | 15 | version = versionTag 16 | group = groupId 17 | 18 | 19 | install { 20 | repositories.mavenInstaller { 21 | pom.project { 22 | packaging 'aar' 23 | } 24 | } 25 | } 26 | 27 | bintray { 28 | user = properties.getProperty("bintray.user") 29 | key = properties.getProperty("bintray.apikey") 30 | configurations = ['archives'] 31 | publish = true 32 | pkg { 33 | repo = 'maven' 34 | name = projectName 35 | desc = 'Ismartv Downloader' 36 | websiteUrl = siteUrl 37 | issueTrackerUrl = 'https://github.com/bintray/bintray-examples/issues' 38 | vcsUrl = gitUrl 39 | licenses = ['Apache-2.0'] 40 | labels = ['aar', 'android'] 41 | publicDownloadNumbers = true 42 | } 43 | } 44 | 45 | 46 | android { 47 | compileSdkVersion rootProject.ext.androidCompileSdkVersion 48 | buildToolsVersion rootProject.ext.androidBuildToolsVersion 49 | 50 | defaultConfig { 51 | minSdkVersion rootProject.ext.androidMinSdkVersion 52 | targetSdkVersion rootProject.ext.androidTargetSdkVersion 53 | versionCode 1 54 | versionName "1.0" 55 | } 56 | buildTypes { 57 | release { 58 | minifyEnabled false 59 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 60 | } 61 | } 62 | } 63 | 64 | dependencies { 65 | Map dependencies = rootProject.ext.dependencies 66 | compile fileTree(include: ['*.jar'], dir: 'libs') 67 | testCompile 'junit:junit:4.12' 68 | compile dependencies.appCompat 69 | compile dependencies.recyclerView 70 | compile dependencies.gridlayout 71 | } -------------------------------------------------------------------------------- /Demo/androidtvwidget/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in F:\dev\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/androidTest/java/com/open/androidtvwidget/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/adapter/BaseTabTitleAdapter.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.adapter; 2 | 3 | import android.view.View; 4 | import android.view.ViewGroup; 5 | import android.widget.BaseAdapter; 6 | 7 | public abstract class BaseTabTitleAdapter extends BaseAdapter { 8 | 9 | @Override 10 | public int getCount() { 11 | return 0; 12 | } 13 | 14 | public Integer getTitleWidgetID(int index) { 15 | return 0; 16 | } 17 | 18 | @Override 19 | public Object getItem(int position) { 20 | return null; 21 | } 22 | 23 | @Override 24 | public long getItemId(int position) { 25 | return 0; 26 | } 27 | 28 | @Override 29 | public View getView(int position, View convertView, ViewGroup parent) { 30 | return null; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/bridge/BaseEffectBridge.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.bridge; 2 | 3 | import com.open.androidtvwidget.view.MainUpView; 4 | 5 | import android.graphics.Canvas; 6 | import android.graphics.Rect; 7 | import android.graphics.RectF; 8 | import android.graphics.drawable.Drawable; 9 | import android.view.View; 10 | 11 | /** 12 | * Anim Bridge 抽象类. ----桥接模式. 13 | * 14 | * @author hailongqiu 356752238@qq.com 15 | * 16 | */ 17 | public abstract class BaseEffectBridge { 18 | 19 | public abstract void onInitBridge(MainUpView view); 20 | 21 | /** 22 | * 需要绘制的东西. 23 | */ 24 | public abstract boolean onDrawMainUpView(Canvas canvas); 25 | 26 | /** 27 | * 老的焦点View. 28 | */ 29 | public abstract void onOldFocusView(View oldFocusView, float scaleX, float scaleY); 30 | 31 | /** 32 | * 新的焦点View. 33 | */ 34 | public abstract void onFocusView(View focusView, float scaleX, float scaleY); 35 | 36 | /** 37 | * 最上层移动的view. 38 | */ 39 | public void setMainUpView(MainUpView view) { 40 | 41 | } 42 | 43 | public MainUpView getMainUpView() { 44 | return null; 45 | } 46 | 47 | /** 48 | * 设置边框图片 49 | */ 50 | 51 | public void setUpRectResource(int resId) { 52 | 53 | } 54 | 55 | public void setUpRectDrawable(Drawable upRectDrawable) { 56 | 57 | } 58 | 59 | public Drawable getUpRectDrawable() { 60 | return null; 61 | } 62 | 63 | public void setDrawUpRectPadding(Rect rect) { 64 | 65 | } 66 | 67 | public void setDrawUpRectPadding(RectF rect) { 68 | 69 | } 70 | 71 | public RectF getDrawUpRect() { 72 | return null; 73 | } 74 | 75 | /** 76 | * 设置边框阴影 77 | */ 78 | 79 | public void setShadowResource(int resId) { 80 | } 81 | 82 | public Drawable getShadowDrawable() { 83 | return null; 84 | } 85 | 86 | public void setShadowDrawable(Drawable shadowDrawable) { 87 | } 88 | 89 | public void setDrawShadowRectPadding(Rect rect) { 90 | } 91 | 92 | public void setDrawShadowRectPadding(RectF rect) { 93 | } 94 | 95 | public RectF getDrawShadowRect() { 96 | return null; 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/bridge/RecyclerViewBridge.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.bridge; 2 | 3 | import android.animation.Animator; 4 | import android.animation.Animator.AnimatorListener; 5 | import android.animation.AnimatorSet; 6 | import android.animation.ObjectAnimator; 7 | import android.graphics.Rect; 8 | import android.graphics.RectF; 9 | import android.support.v7.widget.RecyclerView; 10 | import android.view.View; 11 | import android.view.animation.DecelerateInterpolator; 12 | 13 | import com.open.androidtvwidget.utils.OPENLOG; 14 | 15 | public class RecyclerViewBridge extends EffectNoDrawBridge { 16 | 17 | private AnimatorSet mCurrentAnimatorSet; 18 | 19 | /** 20 | * 重写边框移动函数. 21 | */ 22 | @Override 23 | public void flyWhiteBorder(final View focusView, View moveView, float scaleX, float scaleY) { 24 | RectF paddingRect = getDrawUpRect(); 25 | int newWidth = 0; 26 | int newHeight = 0; 27 | int oldWidth = 0; 28 | int oldHeight = 0; 29 | 30 | int newX = 0; 31 | int newY = 0; 32 | 33 | if (focusView != null) { 34 | newWidth = (int) (focusView.getMeasuredWidth() * scaleX); 35 | newHeight = (int) (focusView.getMeasuredHeight() * scaleY); 36 | oldWidth = moveView.getMeasuredWidth(); 37 | oldHeight = moveView.getMeasuredHeight(); 38 | Rect fromRect = findLocationWithView(moveView); 39 | Rect toRect = findLocationWithView(focusView); 40 | 41 | // 处理 RecyclerView TV 上 移动边框跑偏的问题. 42 | if (null != focusView.getParent() && focusView.getParent() instanceof RecyclerView) { 43 | final RecyclerView rv = (RecyclerView) focusView.getParent(); 44 | final int offset = rv.getBaseline(); 45 | if (offset != -1) { 46 | toRect.offset(rv.getLayoutManager().canScrollHorizontally() ? -offset : 0, 47 | rv.getLayoutManager().canScrollVertically() ? -offset : 0); 48 | } 49 | } 50 | // 51 | int x = toRect.left - fromRect.left - ((int)Math.rint(paddingRect.left)); 52 | int y = toRect.top - fromRect.top - ((int)Math.rint(paddingRect.top)); 53 | newX = x - Math.abs(focusView.getMeasuredWidth() - newWidth) / 2; 54 | newY = y - Math.abs(focusView.getMeasuredHeight() - newHeight) / 2; 55 | // 56 | newWidth += ((int)Math.rint(paddingRect.right) + (int)Math.rint(paddingRect.left)); 57 | newHeight += ((int)Math.rint(paddingRect.bottom) + (int)Math.rint(paddingRect.top)); 58 | } 59 | 60 | // 取消之前的动画. 61 | if (mCurrentAnimatorSet != null) { 62 | mCurrentAnimatorSet.cancel(); 63 | mCurrentAnimatorSet = null; 64 | } 65 | 66 | ObjectAnimator transAnimatorX = ObjectAnimator.ofFloat(moveView, "translationX", newX); 67 | ObjectAnimator transAnimatorY = ObjectAnimator.ofFloat(moveView, "translationY", newY); 68 | // BUG,因为缩放会造成图片失真(拉伸). 69 | // hailong.qiu 2016.02.26 修复 :) 70 | ObjectAnimator scaleXAnimator = ObjectAnimator.ofInt(new ScaleView(moveView), "width", oldWidth, 71 | (int) newWidth); 72 | ObjectAnimator scaleYAnimator = ObjectAnimator.ofInt(new ScaleView(moveView), "height", oldHeight, 73 | (int) newHeight); 74 | // 75 | AnimatorSet mAnimatorSet = new AnimatorSet(); 76 | mAnimatorSet.playTogether(transAnimatorX, transAnimatorY, scaleXAnimator, scaleYAnimator); 77 | mAnimatorSet.setInterpolator(new DecelerateInterpolator(1)); 78 | mAnimatorSet.setDuration(getTranDurAnimTime()); 79 | mAnimatorSet.addListener(new AnimatorListener() { 80 | @Override 81 | public void onAnimationStart(Animator animation) { 82 | if (isVisibleWidget()) { 83 | getMainUpView().setVisibility(View.GONE); 84 | } 85 | if (getNewAnimatorListener() != null) 86 | getNewAnimatorListener().onAnimationStart(RecyclerViewBridge.this, focusView, animation); 87 | } 88 | 89 | @Override 90 | public void onAnimationRepeat(Animator animation) { 91 | } 92 | 93 | @Override 94 | public void onAnimationEnd(Animator animation) { 95 | getMainUpView().setVisibility(isVisibleWidget() ? View.GONE : View.VISIBLE); 96 | if (getNewAnimatorListener() != null) 97 | getNewAnimatorListener().onAnimationEnd(RecyclerViewBridge.this, focusView, animation); 98 | } 99 | 100 | @Override 101 | public void onAnimationCancel(Animator animation) { 102 | } 103 | }); 104 | mAnimatorSet.start(); 105 | mCurrentAnimatorSet = mAnimatorSet; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/cache/BitmapMemoryCache.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.cache; 2 | 3 | import android.graphics.Bitmap; 4 | import android.support.v4.util.LruCache; 5 | 6 | /** 7 | * Bitmap缓存,简单缓存. 8 | * 9 | * @author hailongqiu 356752238@qq.com 10 | * 11 | */ 12 | public class BitmapMemoryCache { 13 | 14 | private static final String TAG = "BitmapMemoryCache"; 15 | 16 | private static BitmapMemoryCache sInstance = new BitmapMemoryCache(); 17 | 18 | private LruCache mMemoryCache; 19 | 20 | /** 21 | * 单例模式. 22 | */ 23 | public static BitmapMemoryCache getInstance() { 24 | return BitmapMemoryCache.sInstance; 25 | } 26 | 27 | private BitmapMemoryCache() { 28 | int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); 29 | int cacheSize = maxMemory / 8; 30 | mMemoryCache = new LruCache(cacheSize) { 31 | @Override 32 | protected int sizeOf(String key, Bitmap bitmap) { 33 | // 重写此方法来衡量每张图片的大小,默认返回图片数量。 34 | return bitmap.getByteCount() / 1024; 35 | } 36 | }; 37 | } 38 | 39 | public void addBitmapToMemoryCache(String key, Bitmap bitmap) { 40 | if (getBitmapFromMemCache(key) == null) { 41 | mMemoryCache.put(key, bitmap); 42 | } 43 | } 44 | 45 | public Bitmap getBitmapFromMemCache(String key) { 46 | return mMemoryCache.get(key); 47 | } 48 | 49 | /** 50 | * 移除缓存 51 | */ 52 | public synchronized void removeImageCache(String key) { 53 | if (key != null) { 54 | if (mMemoryCache != null) { 55 | Bitmap bm = mMemoryCache.remove(key); 56 | if (bm != null) { 57 | bm.recycle(); 58 | } 59 | } 60 | } 61 | } 62 | 63 | } -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/keyboard/KeyRow.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.keyboard; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * 按键的列数组. 8 | * 9 | * @author hailong.qiu 356752238@qq.com 10 | * 11 | */ 12 | public class KeyRow { 13 | 14 | private List mSoftKeys = new ArrayList(); 15 | 16 | public List getSoftKeys() { 17 | return mSoftKeys; 18 | } 19 | 20 | public void setSoftKeys(List mSoftKeys) { 21 | this.mSoftKeys = mSoftKeys; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/keyboard/PopupSoftKey.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.keyboard; 2 | 3 | import java.util.List; 4 | 5 | /*** 6 | * 用于弹出T9键盘或者弹出其它界面的按键. 7 | * 8 | * @author hailongqiu 9 | * 10 | */ 11 | public class PopupSoftKey extends SoftKey { 12 | } 13 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/keyboard/SaveKeyEvent.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.keyboard; 2 | 3 | /** 4 | * 保存了KEYEVENT.KEYCODE_ 的一些按键 CODE. 5 | */ 6 | public class SaveKeyEvent { 7 | /* 8 | KEYCODE_UNKNOWN=0; 9 | KEYCODE_SOFT_LEFT=1; 10 | KEYCODE_SOFT_RIGHT=2; 11 | KEYCODE_HOME=3; 12 | KEYCODE_BACK=4; 13 | KEYCODE_CALL=5; 14 | KEYCODE_ENDCALL=6; 15 | KEYCODE_0=7; 16 | KEYCODE_1=8; 17 | KEYCODE_2=9; 18 | KEYCODE_3=10; 19 | KEYCODE_4=11; 20 | KEYCODE_5=12; 21 | KEYCODE_6=13; 22 | KEYCODE_7=14; 23 | KEYCODE_8=15; 24 | KEYCODE_9=16; 25 | KEYCODE_STAR=17; 26 | KEYCODE_POUND=18; 27 | KEYCODE_DPAD_UP=19; 28 | KEYCODE_DPAD_DOWN=20; 29 | KEYCODE_DPAD_LEFT=21; 30 | KEYCODE_DPAD_RIGHT=22; 31 | KEYCODE_DPAD_CENTER=23; 32 | KEYCODE_VOLUME_UP=24; 33 | KEYCODE_VOLUME_DOWN=25; 34 | KEYCODE_POWER=26; 35 | KEYCODE_CAMERA=27; 36 | KEYCODE_CLEAR=28; 37 | KEYCODE_A=29; 38 | KEYCODE_B=30; 39 | KEYCODE_C=31; 40 | KEYCODE_D=32; 41 | KEYCODE_E=33; 42 | KEYCODE_F=34; 43 | KEYCODE_G=35; 44 | KEYCODE_H=36; 45 | KEYCODE_I=37; 46 | KEYCODE_J=38; 47 | KEYCODE_K=39; 48 | KEYCODE_L=40; 49 | KEYCODE_M=41; 50 | KEYCODE_N=42; 51 | KEYCODE_O=43; 52 | KEYCODE_P=44; 53 | KEYCODE_Q=45; 54 | KEYCODE_R=46; 55 | KEYCODE_S=47; 56 | KEYCODE_T=48; 57 | KEYCODE_U=49; 58 | KEYCODE_V=50; 59 | KEYCODE_W=51; 60 | KEYCODE_X=52; 61 | KEYCODE_Y=53; 62 | KEYCODE_Z=54; 63 | KEYCODE_COMMA=55; 64 | KEYCODE_PERIOD=56; 65 | KEYCODE_ALT_LEFT=57; 66 | KEYCODE_ALT_RIGHT=58; 67 | KEYCODE_SHIFT_LEFT=59; 68 | KEYCODE_SHIFT_RIGHT=60; 69 | KEYCODE_TAB=61; 70 | KEYCODE_SPACE=62; 71 | KEYCODE_SYM=63; 72 | KEYCODE_EXPLORER=64; 73 | KEYCODE_ENVELOPE=65; 74 | KEYCODE_ENTER=66; 75 | KEYCODE_DEL=67; 76 | KEYCODE_GRAVE=68; 77 | KEYCODE_MINUS=69; 78 | KEYCODE_EQUALS=70; 79 | KEYCODE_LEFT_BRACKET=71; 80 | KEYCODE_RIGHT_BRACKET=72; 81 | KEYCODE_BACKSLASH=73; 82 | KEYCODE_SEMICOLON=74; 83 | KEYCODE_APOSTROPHE=75; 84 | KEYCODE_SLASH=76; 85 | KEYCODE_AT=77; 86 | KEYCODE_NUM=78; 87 | KEYCODE_HEADSETHOOK=79; 88 | KEYCODE_FOCUS=80;//*Camera*focus 89 | KEYCODE_PLUS=81; 90 | KEYCODE_MENU=82; 91 | KEYCODE_NOTIFICATION=83; 92 | KEYCODE_SEARCH=84; 93 | KEYCODE_MEDIA_PLAY_PAUSE=85; 94 | KEYCODE_MEDIA_STOP=86; 95 | KEYCODE_MEDIA_NEXT=87; 96 | KEYCODE_MEDIA_PREVIOUS=88; 97 | KEYCODE_MEDIA_REWIND=89; 98 | KEYCODE_MEDIA_FAST_FORWARD=90; 99 | KEYCODE_MUTE=91 100 | */ 101 | } 102 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/keyboard/SkbPool.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.keyboard; 2 | 3 | import android.content.Context; 4 | 5 | /** 6 | * 加载XML键盘布局文件. 7 | * 8 | * @author hailong.qiu 356752238@qq.com 9 | * 10 | */ 11 | public class SkbPool { 12 | 13 | private static final String TAG = "SkbPool"; 14 | 15 | private static SkbPool mInstance = new SkbPool(); // 单例模式. 16 | 17 | private SkbPool() { 18 | } 19 | 20 | public static final SkbPool getInstance() { 21 | return SkbPool.mInstance; 22 | } 23 | 24 | public SoftKeyboard getSoftKeyboard(Context context, int skbXmlId) { 25 | if (null != context) { 26 | XmlKeyboardLoader xkbl = new XmlKeyboardLoader(context); 27 | SoftKeyboard skb = xkbl.loadKeyboard(skbXmlId); 28 | return skb; 29 | } 30 | return null; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/keyboard/SoftKeyBoardListener.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.keyboard; 2 | 3 | public interface SoftKeyBoardListener { 4 | public void onCommitText(SoftKey key); 5 | public void onDelete(SoftKey key); 6 | public void onBack(SoftKey key); 7 | } 8 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/keyboard/SoftKeyBoardable.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.keyboard; 2 | 3 | import android.graphics.RectF; 4 | import android.view.KeyEvent; 5 | 6 | /** 7 | * 键盘接口. 8 | * @author hailongqiu 9 | * 10 | */ 11 | public interface SoftKeyBoardable { 12 | 13 | /** 14 | * 设置选中的按键边框在最前面还是最后面 15 | * @param isFront true 最前面, false 反之 (默认为 false). 16 | */ 17 | public void setSelectSofkKeyFront(boolean isFront); 18 | 19 | /** 20 | * 设置移动边框和按键背景相差的间距. 21 | */ 22 | public void setSoftKeySelectPadding(int padding); 23 | public void setSoftKeySelectPadding(RectF rectf); 24 | 25 | /** 26 | * 设置移动边框的时间. 27 | */ 28 | public void setMoveDuration(int moveDuration); 29 | 30 | /** 31 | * 设置移动边框的标志位. 32 | * @param isMoveRect true : 移动 false : 反之. 33 | */ 34 | public void setMoveSoftKey(boolean isMoveRect); 35 | 36 | public SoftKeyboardView getSoftKeyboardView(); 37 | 38 | /** 39 | * 链接键盘事件处理. 40 | * @param cb 41 | */ 42 | public void setOnSoftKeyBoardListener(SoftKeyBoardListener cb); 43 | 44 | /** 45 | * 设置键盘XML. 46 | * @param layoutID : xml目录下的键盘ID. 47 | */ 48 | public void setSkbLayout(int layoutID); 49 | public int getSkbLayoutId(); 50 | 51 | /** 52 | * 不设置键盘XML,手动编写键盘值. 53 | * @param softSkb 54 | */ 55 | public void setSoftKeyboard(SoftKeyboard softSkb); 56 | 57 | /** 58 | * 设置选中的按键. 59 | * @param softKey 60 | * @return 61 | */ 62 | public boolean setKeySelected(SoftKey softKey); 63 | public void setDefualtSelectKey(int row, int index); 64 | public SoftKey getSelectKey(); 65 | 66 | /** 67 | * 外部处理按键事件. 68 | * @param keyCode 69 | * @param event 70 | * @return 71 | */ 72 | public boolean onSoftKeyDown(int keyCode, KeyEvent event); 73 | public boolean onSoftKeyUp(int keyCode, KeyEvent event); 74 | 75 | /** 76 | * 设置按键缩放. 77 | */ 78 | public void setKeyScale(float scale); 79 | public void setOnKeyBoardAnimListener(SoftKeyboardView.OnKeyBoardAnimListener listener); 80 | 81 | } 82 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/leanback/adapter/GeneralAdapter.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.leanback.adapter; 2 | 3 | import android.support.v7.widget.RecyclerView; 4 | import android.view.View; 5 | import android.view.ViewGroup; 6 | 7 | import com.open.androidtvwidget.leanback.mode.OpenPresenter; 8 | 9 | /** 10 | * RecyclerView 通用 Adapter. 11 | * Created by hailongqiu on 2016/8/22. 12 | */ 13 | public class GeneralAdapter extends RecyclerView.Adapter { 14 | 15 | private OpenPresenter mPresenter; 16 | 17 | public GeneralAdapter(OpenPresenter presenter) { 18 | this.mPresenter = presenter; 19 | if (this.mPresenter != null) 20 | this.mPresenter.setAdapter(this); 21 | } 22 | 23 | public OpenPresenter getPresenter() { 24 | return this.mPresenter; 25 | } 26 | 27 | @Override 28 | public int getItemViewType(int position) { 29 | return this.mPresenter.getItemViewType(position); 30 | } 31 | 32 | @Override 33 | public int getItemCount() { 34 | return this.mPresenter.getItemCount(); 35 | } 36 | 37 | @Override 38 | public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 39 | View view; 40 | OpenPresenter presenter = this.mPresenter; 41 | OpenPresenter.ViewHolder presenterVh; 42 | presenterVh = presenter.onCreateViewHolder(parent, viewType); 43 | view = presenterVh.view; 44 | ViewHolder viewHolder = new ViewHolder(view, presenter, presenterVh); 45 | return viewHolder; 46 | } 47 | 48 | @Override 49 | public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { 50 | ViewHolder viewHolder = (ViewHolder) holder; 51 | viewHolder.mPresenter.onBindViewHolder(viewHolder.getViewHolder(), position); 52 | } 53 | 54 | @Override 55 | public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) { 56 | ViewHolder viewHolder = (ViewHolder) holder; 57 | viewHolder.mPresenter.onViewAttachedToWindow(viewHolder.mHolder); 58 | } 59 | 60 | @Override 61 | public void onViewDetachedFromWindow(RecyclerView.ViewHolder holder) { 62 | ViewHolder viewHolder = (ViewHolder) holder; 63 | viewHolder.mPresenter.onViewDetachedFromWindow(viewHolder.mHolder); 64 | } 65 | 66 | @Override 67 | public void onViewRecycled(RecyclerView.ViewHolder holder) { 68 | ViewHolder viewHolder = (ViewHolder) holder; 69 | viewHolder.mPresenter.onUnbindViewHolder(viewHolder.mHolder); 70 | } 71 | 72 | public class ViewHolder extends RecyclerView.ViewHolder { 73 | 74 | final OpenPresenter.ViewHolder mHolder; 75 | final OpenPresenter mPresenter; 76 | 77 | public ViewHolder(View itemView, OpenPresenter presenter, OpenPresenter.ViewHolder holder) { 78 | super(itemView); 79 | this.mPresenter = presenter; 80 | this.mHolder = holder; 81 | } 82 | 83 | public OpenPresenter.ViewHolder getViewHolder() { 84 | return this.mHolder; 85 | } 86 | 87 | public OpenPresenter getOpenPresenter() { 88 | return this.mPresenter; 89 | } 90 | 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/leanback/mode/DefualtListPresenter.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.leanback.mode; 2 | 3 | import android.content.Context; 4 | import android.support.v7.widget.LinearLayoutManager; 5 | import android.support.v7.widget.RecyclerView; 6 | import android.view.ViewGroup; 7 | import android.widget.Button; 8 | 9 | import com.open.androidtvwidget.leanback.recycle.LinearLayoutManagerTV; 10 | import com.open.androidtvwidget.leanback.recycle.RecyclerViewTV; 11 | 12 | import java.util.List; 13 | 14 | /** 15 | * 如果你要设置自己的 横向 RecyclerView 中的item样式,继承这个. 16 | * Created by hailongqiu on 2016/8/24. 17 | */ 18 | public class DefualtListPresenter extends OpenPresenter { 19 | 20 | private static final float DEFUALT_SCALE = 1.2f; 21 | 22 | List mItems; 23 | private RecyclerViewTV.OnItemListener mOnItemListener; 24 | private RecyclerViewTV.OnItemClickListener mOnItemClickListener; // item 单击事件. 25 | 26 | public DefualtListPresenter() { 27 | 28 | } 29 | 30 | /** 31 | * 返回自己的 LayoutMAnger. 32 | */ 33 | public RecyclerView.LayoutManager getLayoutManger(Context context) { 34 | LinearLayoutManagerTV layoutManager = new LinearLayoutManagerTV(context, LinearLayoutManager.HORIZONTAL, false); 35 | layoutManager.setAutoMeasureEnabled(true); // 自动适应布局. 36 | return layoutManager; 37 | } 38 | 39 | public float getScaleX() { 40 | return DEFUALT_SCALE; 41 | } 42 | 43 | public float getScanleY() { 44 | return DEFUALT_SCALE; 45 | } 46 | 47 | public RecyclerViewTV.OnItemListener getOnItemListener() { 48 | return mOnItemListener; 49 | } 50 | 51 | public RecyclerViewTV.OnItemClickListener getOnItemClickListener() { 52 | return mOnItemClickListener; 53 | } 54 | 55 | public void setOnItemListener(RecyclerViewTV.OnItemListener onItemListener) { 56 | this.mOnItemListener = onItemListener; 57 | } 58 | 59 | public void setOnItemClickListener(RecyclerViewTV.OnItemClickListener onItemClickListener) { 60 | this.mOnItemClickListener = onItemClickListener; 61 | } 62 | 63 | public DefualtListPresenter(Object items) { 64 | mItems = (List) items; 65 | } 66 | 67 | public void setItems(Object items) { 68 | mItems = (List) items; 69 | } 70 | 71 | @Override 72 | public int getItemCount() { 73 | return mItems.size(); 74 | } 75 | 76 | public Object getItem(int pos) { 77 | return this.mItems.get(pos); 78 | } 79 | 80 | @Override 81 | public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 82 | Button btn = new Button(parent.getContext()); 83 | return new ViewHolder(btn); 84 | } 85 | 86 | @Override 87 | public void onBindViewHolder(ViewHolder viewHolder, int position) { 88 | String str = ((String) mItems.get(position)); 89 | Button btn = ((Button) viewHolder.view); 90 | btn.setText(str); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/leanback/mode/ItemHeaderPresenter.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.leanback.mode; 2 | 3 | import android.view.LayoutInflater; 4 | import android.view.View; 5 | import android.view.ViewGroup; 6 | import android.widget.LinearLayout; 7 | 8 | import com.open.androidtvwidget.R; 9 | import com.open.androidtvwidget.menu.OpenMenuItemView; 10 | 11 | /** 12 | * 标题头的 Presenter. (test) 13 | * Created by hailongqiu on 2016/8/24. 14 | */ 15 | public class ItemHeaderPresenter extends OpenPresenter { 16 | 17 | @Override 18 | public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 19 | View headview = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_menu_item_layout, parent, false); 20 | return new ItemHeadViewHolder(headview); 21 | } 22 | 23 | @Override 24 | public void onBindViewHolder(ViewHolder viewHolder, Object item) { 25 | LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) viewHolder.view.getLayoutParams(); 26 | lp.weight = LinearLayout.LayoutParams.MATCH_PARENT; 27 | ((OpenMenuItemView) viewHolder.view).setTitle((String)item); 28 | } 29 | 30 | static class ItemHeadViewHolder extends OpenPresenter.ViewHolder { 31 | public ItemHeadViewHolder(View view) { 32 | super(view); 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/leanback/mode/ItemListPresenter.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.leanback.mode; 2 | 3 | import android.view.View; 4 | import android.view.ViewGroup; 5 | 6 | import com.open.androidtvwidget.leanback.adapter.GeneralAdapter; 7 | import com.open.androidtvwidget.leanback.recycle.RecyclerViewTV; 8 | import com.open.androidtvwidget.leanback.widget.ListContentView; 9 | 10 | /** 11 | * Leanback item 标题头下面的 横向布局.(要弄自己的样式,继承这个,重写),这里只是规定了横向的. 12 | * Created by hailongqiu on 2016/8/24. 13 | */ 14 | public class ItemListPresenter extends OpenPresenter { 15 | 16 | @Override 17 | public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 18 | ListContentView listContentView = new ListContentView(parent.getContext()); 19 | return new ItemListViewHolder(listContentView, listContentView.getRecyclerViewTV()); 20 | } 21 | 22 | @Override 23 | public void onBindViewHolder(ViewHolder viewHolder, final Object item) { 24 | final ItemListViewHolder itemListViewHolder = (ItemListViewHolder) viewHolder; 25 | ListRow listRow = (ListRow) item; 26 | DefualtListPresenter openPresenter = (DefualtListPresenter) listRow.getOpenPresenter(); 27 | itemListViewHolder.setDefualtListPresenter(openPresenter); // 保存一下,外面需要调用. 28 | openPresenter.setItems(listRow.getItems()); 29 | itemListViewHolder.mRecyclerViewTV.setLayoutManager(openPresenter.getLayoutManger(viewHolder.view.getContext())); 30 | GeneralAdapter generalAdapter = new GeneralAdapter(openPresenter); 31 | itemListViewHolder.mRecyclerViewTV.setAdapter(generalAdapter); 32 | itemListViewHolder.mRecyclerViewTV.setOnItemListener(openPresenter.getOnItemListener()); 33 | itemListViewHolder.mRecyclerViewTV.setOnItemClickListener(openPresenter.getOnItemClickListener()); 34 | } 35 | 36 | public static class ItemListViewHolder extends OpenPresenter.ViewHolder { 37 | private RecyclerViewTV mRecyclerViewTV; 38 | private DefualtListPresenter mDefualtListPresenter; 39 | 40 | public ItemListViewHolder(View view, RecyclerViewTV rv) { 41 | super(view); 42 | this.mRecyclerViewTV = rv; 43 | } 44 | 45 | public void setDefualtListPresenter(DefualtListPresenter presenter) { 46 | this.mDefualtListPresenter = presenter; 47 | } 48 | 49 | public DefualtListPresenter getDefualtListPresenter() { 50 | return this.mDefualtListPresenter; 51 | } 52 | 53 | public RecyclerViewTV getRecyclerViewTV() { 54 | return this.mRecyclerViewTV; 55 | } 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/leanback/mode/ListRow.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.leanback.mode; 2 | 3 | import com.open.androidtvwidget.utils.OPENLOG; 4 | 5 | import java.util.ArrayList; 6 | import java.util.Collection; 7 | import java.util.List; 8 | 9 | /** 10 | * Leanback 数据结构. 11 | * Created by hailongqiu on 2016/8/24. 12 | */ 13 | public class ListRow { 14 | private Object mHeaderItem; 15 | private DefualtListPresenter mOpenPresenter; 16 | private List mItems = new ArrayList(); 17 | 18 | public ListRow(Object header) { 19 | this.mHeaderItem = header; 20 | } 21 | 22 | public final Object getHeaderItem() { 23 | return this.mHeaderItem; 24 | } 25 | 26 | public final void setHeaderItem(Object headerItem) { 27 | mHeaderItem = headerItem; 28 | } 29 | 30 | public void add(Object item) { 31 | mItems.add(item); 32 | } 33 | 34 | public void addAll(Object items) { 35 | this.mItems.addAll((Collection) items); 36 | } 37 | 38 | public List getItems() { 39 | return this.mItems; 40 | } 41 | 42 | public void setOpenPresenter(DefualtListPresenter openPresenter) { 43 | this.mOpenPresenter = openPresenter; 44 | } 45 | 46 | public DefualtListPresenter getOpenPresenter() { 47 | return this.mOpenPresenter; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/leanback/mode/ListRowPresenter.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.leanback.mode; 2 | 3 | import android.view.View; 4 | import android.view.ViewGroup; 5 | 6 | import com.open.androidtvwidget.leanback.adapter.GeneralAdapter; 7 | import com.open.androidtvwidget.leanback.widget.ItemContainerView; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * Leanback ListRowPresenter 控制层. 13 | * Created by hailongqiu on 2016/8/24. 14 | */ 15 | public class ListRowPresenter extends OpenPresenter { 16 | 17 | OpenPresenter mItemHeaderPresenter;// item 标题头的 Presenter. 18 | OpenPresenter mItemListPresenter;// item 标题头下面的 横向 items. 19 | 20 | List mItems; 21 | GeneralAdapter mAdapter; 22 | 23 | private int mColumn; 24 | private int mRow; 25 | private int mTime = 188; 26 | 27 | public void setDefaultPos(int row, int column) { 28 | setDelayDefaultPos(row, column, 188); 29 | } 30 | 31 | public void setDelayDefaultPos(int row, int column, int time) { 32 | this.mRow = row; 33 | this.mColumn = column; 34 | this.mTime = time; 35 | } 36 | 37 | /** 38 | * 你可以设置自己的 头 presenter, 还有横向 presenter. 39 | * 40 | * @param items 41 | * @param headPresenter 42 | * @param listPresenter 43 | */ 44 | public ListRowPresenter(List items, OpenPresenter headPresenter, OpenPresenter listPresenter) { 45 | this.mItems = items; 46 | this.mItemHeaderPresenter = headPresenter; 47 | this.mItemListPresenter = listPresenter; 48 | } 49 | 50 | public ListRowPresenter(List items) { 51 | this(items, null, null); 52 | } 53 | 54 | @Override 55 | public void setAdapter(GeneralAdapter adapter) { 56 | this.mAdapter = adapter; 57 | } 58 | 59 | public void setItems(List items, int position) { 60 | this.mItems = items; 61 | if (this.mAdapter != null) 62 | this.mAdapter.notifyItemChanged(position); 63 | } 64 | 65 | public void setItems(List items) { 66 | this.mItems = items; 67 | if (this.mAdapter != null) 68 | this.mAdapter.notifyDataSetChanged(); 69 | } 70 | 71 | public List getItems() { 72 | return this.mItems; 73 | } 74 | 75 | @Override 76 | public int getItemCount() { 77 | return mItems != null ? mItems.size() : 0; 78 | } 79 | 80 | @Override 81 | public int getItemViewType(int position) { 82 | return 0; 83 | } 84 | 85 | /** 86 | * 设置标题头的Presenter. 87 | */ 88 | public void setHeadPresenter(OpenPresenter presenter) { 89 | this.mItemHeaderPresenter = presenter; 90 | } 91 | 92 | @Override 93 | public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 94 | ItemContainerView itemContainerView = new ItemContainerView(parent.getContext()); 95 | // 添加标题头. 96 | ViewHolder headVH = null; 97 | if (mItemHeaderPresenter != null) { 98 | headVH = mItemHeaderPresenter.onCreateViewHolder(parent, viewType); 99 | itemContainerView.addHeaderView(headVH.view); 100 | } 101 | // 添加横向控件. 102 | ViewHolder listVH = null; 103 | if (mItemListPresenter != null) { 104 | listVH = mItemListPresenter.onCreateViewHolder(parent, viewType); 105 | itemContainerView.addRowView(listVH.view); 106 | } 107 | // 108 | return new ListRowViewHolder(itemContainerView, headVH, listVH); 109 | } 110 | 111 | @Override 112 | public void onBindViewHolder(ViewHolder viewHolder, int position) { 113 | ListRowViewHolder listRowViewHolder = (ListRowViewHolder) viewHolder; 114 | ListRow listRow = mItems.get(position); 115 | 116 | if (listRowViewHolder.mHeadViewHolder != null) { 117 | mItemHeaderPresenter.onBindViewHolder(listRowViewHolder.mHeadViewHolder, listRow.getHeaderItem()); 118 | } 119 | 120 | if (listRowViewHolder.mListViewHolder != null) { 121 | if (mRow == position) { 122 | ItemListPresenter.ItemListViewHolder itemListViewHolder = (ItemListPresenter.ItemListViewHolder) listRowViewHolder.mListViewHolder; 123 | itemListViewHolder.getRecyclerViewTV().setDelayDefaultSelect(mColumn, this.mTime); 124 | mColumn = -1; 125 | mRow = -1; 126 | } 127 | mItemListPresenter.onBindViewHolder(listRowViewHolder.mListViewHolder, listRow); 128 | } 129 | 130 | } 131 | 132 | public static class ListRowViewHolder extends OpenPresenter.ViewHolder { 133 | 134 | ViewHolder mHeadViewHolder; 135 | ViewHolder mListViewHolder; 136 | 137 | public ListRowViewHolder(View view, ViewHolder headVH, ViewHolder lilstVH) { 138 | super(view); 139 | this.mHeadViewHolder = headVH; 140 | this.mListViewHolder = lilstVH; 141 | } 142 | 143 | public ViewHolder getListViewHolder() { 144 | return mListViewHolder; 145 | } 146 | } 147 | 148 | } 149 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/leanback/mode/OpenPresenter.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.leanback.mode; 2 | 3 | import android.view.View; 4 | import android.view.ViewGroup; 5 | 6 | import com.open.androidtvwidget.leanback.adapter.GeneralAdapter; 7 | 8 | /** 9 | * Created by hailongqiu on 2016/8/22. 10 | */ 11 | public abstract class OpenPresenter { 12 | 13 | /** 14 | * 基本数据类型(ViewHolder) 15 | */ 16 | public static class ViewHolder { 17 | 18 | public final View view; 19 | 20 | public ViewHolder(View view) { 21 | this.view = view; 22 | } 23 | } 24 | 25 | public int getItemCount() { 26 | return 0; 27 | } 28 | 29 | public int getItemViewType(int position) { 30 | return 0; 31 | } 32 | 33 | public abstract ViewHolder onCreateViewHolder(ViewGroup parent, int viewType); 34 | 35 | public void onViewAttachedToWindow(ViewHolder viewHolder) { 36 | 37 | } 38 | 39 | public void onBindViewHolder(ViewHolder viewHolder, int position) { 40 | 41 | } 42 | 43 | public void onBindViewHolder(ViewHolder viewHolder, Object item) { 44 | 45 | } 46 | 47 | public void onViewDetachedFromWindow(ViewHolder viewHolder) { 48 | 49 | } 50 | 51 | public void onUnbindViewHolder(ViewHolder viewHolder) { 52 | 53 | } 54 | 55 | public void setAdapter(GeneralAdapter adapter) { 56 | 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/leanback/recycle/AutoMeaureGridLayoutManager.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.leanback.recycle; 2 | 3 | import android.content.Context; 4 | import android.support.v7.widget.GridLayoutManager; 5 | import android.support.v7.widget.RecyclerView; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | 9 | import com.open.androidtvwidget.utils.OPENLOG; 10 | 11 | /** 12 | * Created by hailongqiu on 2016/8/25. 13 | */ 14 | public class AutoMeaureGridLayoutManager extends GridLayoutManager { 15 | 16 | public AutoMeaureGridLayoutManager(Context context, int spanCount) { 17 | super(context, spanCount); 18 | } 19 | 20 | public AutoMeaureGridLayoutManager(Context context, int spanCount, int orientation, boolean reverseLayout) { 21 | super(context, spanCount, orientation, reverseLayout); 22 | } 23 | 24 | private int[] mMeasuredDimension = new int[2]; 25 | private boolean mIsAutoMeaure = true; 26 | 27 | // @Override 28 | // public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) { 29 | // 30 | // if (!mIsAutoMeaure) { 31 | // super.onMeasure(recycler, state, widthSpec, heightSpec); 32 | // } else { 33 | // 34 | // final int widthMode = View.MeasureSpec.getMode(widthSpec); 35 | // final int heightMode = View.MeasureSpec.getMode(heightSpec); 36 | // final int widthSize = View.MeasureSpec.getSize(widthSpec); 37 | // final int heightSize = View.MeasureSpec.getSize(heightSpec); 38 | // 39 | // int width = 0; 40 | // int height = 0; 41 | // 42 | // // 43 | // for (int i = 0; i < getItemCount(); i++) { 44 | // // grid layout 计算. 45 | // try { 46 | // measureScrapChild(recycler, i, 47 | // widthSpec, 48 | // View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED), 49 | // mMeasuredDimension); 50 | // } catch (IndexOutOfBoundsException e) { 51 | // e.printStackTrace(); 52 | // } 53 | // if (getOrientation() == VERTICAL) { 54 | // if (i % getSpanCount() != 0) { 55 | // width += mMeasuredDimension[0]; 56 | // } 57 | // if ((i % getSpanCount()) == 0) { 58 | // height += mMeasuredDimension[1]; 59 | // } 60 | // } else { 61 | // if (i % getSpanCount() == 0) { 62 | // height += mMeasuredDimension[1]; 63 | // width += mMeasuredDimension[0] + 2; 64 | // } 65 | // } 66 | // } 67 | // 68 | // switch (widthMode) { 69 | // case View.MeasureSpec.UNSPECIFIED: 70 | // break; 71 | // case View.MeasureSpec.AT_MOST: // wrap_parent 72 | // width = widthSize; 73 | // break; 74 | // case View.MeasureSpec.EXACTLY: // match_parent 75 | // width = widthSize; 76 | // break; 77 | // } 78 | // 79 | // switch (heightMode) { 80 | // case View.MeasureSpec.UNSPECIFIED: 81 | // break; 82 | // case View.MeasureSpec.AT_MOST: // wrap_parent 83 | //// height = heightSize; 84 | // break; 85 | // case View.MeasureSpec.EXACTLY: // match_parent 86 | // height = heightSize; 87 | // break; 88 | // } 89 | // // 90 | // setMeasuredDimension(width, height); 91 | // } 92 | // 93 | // } 94 | 95 | private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec, int heightSpec, int[] measuredDimension) { 96 | View view = recycler.getViewForPosition(position); 97 | if (view != null) { 98 | RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams(); 99 | int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec, 100 | getPaddingTop() + getPaddingBottom(), p.height); 101 | view.measure(widthSpec, childHeightSpec); 102 | measuredDimension[0] = view.getMeasuredWidth() + p.leftMargin + p.rightMargin; 103 | measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin; 104 | recycler.recycleView(view); 105 | } 106 | } 107 | 108 | public void setAutoMeasureEnabled(boolean isAutoMeaure) { 109 | this.mIsAutoMeaure = isAutoMeaure; 110 | } 111 | 112 | @Override 113 | public View onFocusSearchFailed(View focused, int focusDirection, RecyclerView.Recycler recycler, RecyclerView.State state) { 114 | View nextFocus = super.onFocusSearchFailed(focused, focusDirection, recycler, state); 115 | return null; 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/leanback/recycle/GridLayoutManagerTV.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.leanback.recycle; 2 | 3 | import android.content.Context; 4 | import android.support.v7.widget.GridLayoutManager; 5 | import android.support.v7.widget.RecyclerView; 6 | import android.util.AttributeSet; 7 | import android.view.View; 8 | 9 | /** 10 | * Created by hailongqiu on 2016/8/25. 11 | */ 12 | public class GridLayoutManagerTV extends GridLayoutManager { 13 | public GridLayoutManagerTV(Context context, int spanCount) { 14 | super(context, spanCount); 15 | } 16 | 17 | public GridLayoutManagerTV(Context context, int spanCount, int orientation, boolean reverseLayout) { 18 | super(context, spanCount, orientation, reverseLayout); 19 | } 20 | 21 | public GridLayoutManagerTV(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 22 | super(context, attrs, defStyleAttr, defStyleRes); 23 | } 24 | 25 | @Override 26 | public View onFocusSearchFailed(View focused, int focusDirection, RecyclerView.Recycler recycler, RecyclerView.State state) { 27 | View nextFocus = super.onFocusSearchFailed(focused, focusDirection, recycler, state); 28 | return null; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/leanback/recycle/Helper/ItemKeyHelper.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.leanback.recycle.Helper; 2 | 3 | import android.support.v7.widget.RecyclerView; 4 | import android.support.v7.widget.helper.ItemTouchHelper; 5 | import android.view.KeyEvent; 6 | 7 | import com.open.androidtvwidget.leanback.recycle.RecyclerViewTV; 8 | 9 | /** 10 | * 按键拖动item. 11 | * Created by hailongqiu on 2016/9/6. 12 | */ 13 | public class ItemKeyHelper extends ItemTouchHelper { 14 | 15 | public ItemKeyHelper(Callback callback) { 16 | super(callback); 17 | } 18 | 19 | @Override 20 | public void attachToRecyclerView(RecyclerView recyclerView) { 21 | super.attachToRecyclerView(recyclerView); 22 | if (recyclerView instanceof RecyclerViewTV) { 23 | RecyclerViewTV recyclerViewTV = (RecyclerViewTV) recyclerView; 24 | recyclerViewTV.addOnItemKeyListener(mOnItemKeyListener); 25 | } 26 | } 27 | 28 | private final RecyclerViewTV.OnItemKeyListener mOnItemKeyListener 29 | = new RecyclerViewTV.OnItemKeyListener() { 30 | @Override 31 | public boolean dispatchKeyEvent(KeyEvent event) { 32 | return false; 33 | } 34 | }; 35 | 36 | } 37 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/leanback/recycle/impl/PrvInterface.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.leanback.recycle.impl; 2 | 3 | import com.open.androidtvwidget.leanback.recycle.RecyclerViewTV; 4 | 5 | /** 6 | * 按键加载更多接口. 7 | * Created by hailongqiu on 2016/9/5. 8 | */ 9 | public interface PrvInterface { 10 | 11 | void setOnLoadMoreComplete(); // 按键加载更多-->完成. 12 | void setPagingableListener(RecyclerViewTV.PagingableListener pagingableListener); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/leanback/widget/ItemContainerView.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.leanback.widget; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.LinearLayout; 9 | 10 | import com.open.androidtvwidget.R; 11 | 12 | /** 13 | * Leanback 的一个item-->添加上面的标题头,添加下面的横向. 14 | * Created by hailongqiu on 2016/8/22. 15 | */ 16 | public class ItemContainerView extends LinearLayout { 17 | 18 | private ViewGroup mHeadDock; 19 | 20 | public ItemContainerView(Context context) { 21 | this(context, null, 0); 22 | } 23 | 24 | public ItemContainerView(Context context, AttributeSet attrs) { 25 | this(context, attrs, 0); 26 | } 27 | 28 | public ItemContainerView(Context context, AttributeSet attrs, int defStyleAttr) { 29 | super(context, attrs, defStyleAttr); 30 | setOrientation(VERTICAL); 31 | LayoutInflater inflater = LayoutInflater.from(context); 32 | inflater.inflate(R.layout.lb_item_container, this); 33 | mHeadDock = (ViewGroup) findViewById(R.id.lb_item_container_head_dock); 34 | setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); 35 | // 36 | setClipChildren(false); 37 | setClipToPadding(false); 38 | } 39 | 40 | public void addHeaderView(View headerView) { 41 | if (mHeadDock.indexOfChild(headerView) < 0) { 42 | mHeadDock.addView(headerView, 0); 43 | } 44 | } 45 | 46 | public void removeHeaderView(View headerView) { 47 | if (mHeadDock.indexOfChild(headerView) >= 0) { 48 | mHeadDock.removeView(headerView); 49 | } 50 | } 51 | 52 | public void addRowView(View view) { 53 | addView(view); 54 | } 55 | 56 | public void showHeader(boolean show) { 57 | mHeadDock.setVisibility(show ? View.VISIBLE : View.GONE); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/leanback/widget/ListContentView.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.leanback.widget; 2 | 3 | import android.content.Context; 4 | import android.support.v7.widget.LinearLayoutManager; 5 | import android.util.AttributeSet; 6 | import android.view.LayoutInflater; 7 | import android.view.ViewGroup; 8 | import android.widget.LinearLayout; 9 | 10 | import com.open.androidtvwidget.R; 11 | import com.open.androidtvwidget.leanback.recycle.RecyclerViewTV; 12 | 13 | /** 14 | * MenuContentView 是 Leanback 一个item 标题头下面的横向RecyclerView. 15 | * Created by hailongqiu on 2016/8/22. 16 | */ 17 | public class ListContentView extends LinearLayout { 18 | 19 | private RecyclerViewTV mRecyclerViewTV; 20 | 21 | public ListContentView(Context context) { 22 | this(context, null); 23 | } 24 | 25 | public ListContentView(Context context, AttributeSet attrs) { 26 | this(context, attrs, 0); 27 | } 28 | 29 | public ListContentView(Context context, AttributeSet attrs, int defStyle) { 30 | super(context, attrs, defStyle); 31 | // 32 | LayoutInflater inflater = LayoutInflater.from(context); 33 | inflater.inflate(R.layout.lb_content_view, this); 34 | mRecyclerViewTV = (RecyclerViewTV) findViewById(R.id.menu_content); 35 | // 36 | setOrientation(LinearLayout.VERTICAL); 37 | // 先分发给Child View进行处理,如果所有的Child View都没有处理,则自己再处理 38 | setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); 39 | // 让放大的item可以显示出来. 40 | setClipChildren(false); 41 | setClipToPadding(false); 42 | } 43 | 44 | public void setLayoutManager(LinearLayoutManager lm) { 45 | mRecyclerViewTV.setLayoutManager(lm); 46 | } 47 | 48 | public RecyclerViewTV getRecyclerViewTV() { 49 | return this.mRecyclerViewTV; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/leanback/widget/NORLinearLayout.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.leanback.widget; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.widget.LinearLayout; 6 | 7 | /** 8 | * 优化过的LinerLayout. --> hasOverlappingRendering (false) 9 | * Created by hailongqiu on 2016/8/22. 10 | */ 11 | public class NORLinearLayout extends LinearLayout { 12 | public NORLinearLayout(Context context) { 13 | this(context, null); 14 | } 15 | 16 | public NORLinearLayout(Context context, AttributeSet attrs) { 17 | this(context, attrs, 0); 18 | } 19 | 20 | public NORLinearLayout(Context context, AttributeSet attrs, int defStyle) { 21 | super(context, attrs, defStyle); 22 | } 23 | 24 | @Override 25 | public boolean hasOverlappingRendering() { 26 | return false; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/menu/MenuDataObservable.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.menu; 2 | 3 | import android.database.Observable; 4 | 5 | /** 6 | * 观察者模式. 7 | * Created by hailongqiu on 2016/6/2. 8 | */ 9 | public class MenuDataObservable extends Observable { 10 | 11 | /** 12 | * 显示菜单改变. 13 | */ 14 | public void nofityShow(OpenMenu openMenu) { 15 | synchronized (mObservers) { 16 | for (int i = mObservers.size() - 1; i >= 0; i--) { 17 | mObservers.get(i).onShow(openMenu); 18 | } 19 | } 20 | } 21 | 22 | /** 23 | * 隐藏菜单的改变. 24 | */ 25 | public void notifyHide(OpenMenu openMenu) { 26 | synchronized (mObservers) { 27 | for (int i = mObservers.size() - 1; i >= 0; i--) { 28 | mObservers.get(i).onHide(openMenu); 29 | } 30 | } 31 | } 32 | 33 | /** 34 | * 数据改变. 35 | */ 36 | public void notifyChanged(OpenMenu openMenu) { 37 | synchronized (mObservers) { 38 | for (int i = mObservers.size() - 1; i >= 0; i--) { 39 | mObservers.get(i).onChanged(openMenu); 40 | } 41 | } 42 | } 43 | 44 | /** 45 | * 数据改变. 46 | */ 47 | public void notifyChanged() { 48 | synchronized (mObservers) { 49 | for (int i = mObservers.size() - 1; i >= 0; i--) { 50 | mObservers.get(i).onChanged(); 51 | } 52 | } 53 | } 54 | 55 | public void notifyInvalidated() { 56 | synchronized (mObservers) { 57 | for (int i = mObservers.size() - 1; i >= 0; i--) { 58 | mObservers.get(i).onInvalidated(); 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/menu/MenuSetObserver.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.menu; 2 | 3 | /** 4 | * Created by hailongqiu on 2016/6/2. 5 | */ 6 | public abstract class MenuSetObserver { 7 | public void onShow(OpenMenu openMenu) { 8 | } 9 | 10 | public void onHide(OpenMenu openMenu) { 11 | } 12 | 13 | public void onChanged(OpenMenu openMenu) { 14 | } 15 | 16 | public void onChanged() { 17 | } 18 | 19 | public void onInvalidated() { 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/menu/OpenMenu.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.menu; 2 | 3 | import java.util.ArrayList; 4 | 5 | /** 6 | * 菜单接口. 7 | * 8 | * @author hailongqiu 9 | */ 10 | public interface OpenMenu { 11 | 12 | static final int NONE = 0; 13 | 14 | /** 15 | * 添加菜单项. 16 | */ 17 | public OpenMenuItem add(CharSequence title); 18 | 19 | /** 20 | * 添加子菜单. 21 | */ 22 | public OpenMenu addSubMenu(int pos, OpenMenu openSubMenu); // 添加子菜单到某个位置的菜单上. 23 | 24 | public OpenMenu addSubMenu(OpenMenuItem menuItem, OpenMenu openSubMenu); 25 | 26 | public OpenMenu setTextSize(int size); // 全局设置菜单字体. 27 | 28 | public ArrayList getMenuDatas(); // 获取菜单数据. 29 | 30 | public void setParentMenu(OpenMenu openMenu); 31 | 32 | public OpenMenu getParentMenu(); 33 | 34 | /** 35 | * 注册/注销----观察者 36 | */ 37 | void registerDataSetObserver(MenuSetObserver observer); 38 | 39 | void unregisterDataSetObserver(MenuSetObserver observer); 40 | 41 | MenuDataObservable getMenuDataObservable(); // 获取监听者. 42 | 43 | void showMenu(); 44 | 45 | void hideMenu(); 46 | 47 | /** 48 | * 获取层级. 49 | */ 50 | int getTreeDepth(); 51 | 52 | /** 53 | * 数据更新. 54 | */ 55 | public void notifyChanged(); 56 | 57 | } 58 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/menu/OpenMenuImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 The Open Source Project 3 | 4 | Author: hailongqiu <356752238@qq.com> 5 | Maintainer: hailongqiu <356752238@qq.com> 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | */ 19 | package com.open.androidtvwidget.menu; 20 | 21 | import com.open.androidtvwidget.utils.OPENLOG; 22 | 23 | import java.util.ArrayList; 24 | 25 | /** 26 | * 菜单. 27 | * 28 | * @author hailongqiu 29 | */ 30 | public class OpenMenuImpl implements OpenMenu { 31 | 32 | private static final String TAG = "OpenMenu"; 33 | 34 | private final MenuDataObservable mMenuDataObservable = new MenuDataObservable(); // 观察者. 35 | 36 | private ArrayList mItems; 37 | private OpenMenu mParent; 38 | 39 | private int mTextSize = OpenMenuItem.DEFAULT_TEXT_SIZE; 40 | 41 | public OpenMenuImpl() { 42 | init(); 43 | } 44 | 45 | @Override 46 | public void setParentMenu(OpenMenu openMenu) { 47 | mParent = openMenu; 48 | } 49 | 50 | @Override 51 | public OpenMenu getParentMenu() { 52 | return this.mParent; 53 | } 54 | 55 | @Override 56 | public void registerDataSetObserver(MenuSetObserver observer) { 57 | mMenuDataObservable.registerObserver(observer); 58 | } 59 | 60 | @Override 61 | public void unregisterDataSetObserver(MenuSetObserver observer) { 62 | mMenuDataObservable.unregisterObserver(observer); 63 | } 64 | 65 | @Override 66 | public MenuDataObservable getMenuDataObservable() { 67 | return this.mMenuDataObservable; 68 | } 69 | 70 | @Override 71 | public void showMenu() { 72 | if (mMenuDataObservable != null) 73 | mMenuDataObservable.nofityShow(this); 74 | } 75 | 76 | @Override 77 | public void hideMenu() { 78 | if (mMenuDataObservable != null) 79 | mMenuDataObservable.notifyHide(this); 80 | } 81 | 82 | @Override 83 | public int getTreeDepth() { 84 | return this.mParent == null ? 0 : this.mParent.getTreeDepth() + 1; 85 | } 86 | 87 | @Override 88 | public void notifyChanged() { 89 | if (mMenuDataObservable != null) 90 | mMenuDataObservable.notifyChanged(this); 91 | } 92 | 93 | private void init() { 94 | this.mItems = new ArrayList(); 95 | } 96 | 97 | @Override 98 | public OpenMenu setTextSize(int size) { 99 | this.mTextSize = size; 100 | return this; 101 | } 102 | 103 | private OpenMenuItem addInternal(int itemId, CharSequence title) { 104 | final OpenMenuItem item = new OpenMenuItemImpl(this, itemId, title); 105 | item.setTextSize(mTextSize); 106 | mItems.add(item); 107 | return item; 108 | } 109 | 110 | @Override 111 | public OpenMenuItem add(CharSequence title) { 112 | return addInternal(0, title); 113 | } 114 | 115 | /** 116 | * 添加子菜单. 117 | */ 118 | @Override 119 | public OpenMenu addSubMenu(int pos, OpenMenu openSubMenu) { 120 | if (mItems != null && pos < mItems.size()) { 121 | OpenMenuItem menuItem = mItems.get(pos); 122 | addSubMenu(menuItem, openSubMenu); 123 | } 124 | return openSubMenu; 125 | } 126 | 127 | public OpenMenu addSubMenu(OpenMenuItem menuItem, OpenMenu openSubMenu) { 128 | menuItem.addSubMenu(openSubMenu); 129 | return openSubMenu; 130 | } 131 | 132 | /** 133 | * 设置数据. 134 | */ 135 | @Override 136 | public String toString() { 137 | for (OpenMenuItem item : mItems) { 138 | String title = item.getTitle().toString(); 139 | OPENLOG.E("menu item:" + title + " 深度:" + item.getMenu().getTreeDepth()); 140 | OpenMenu submenu = item.getSubMenu(); 141 | if (submenu != null) { 142 | OPENLOG.E("=======sub menu======start start start start"); 143 | submenu.toString(); 144 | OPENLOG.E("=======sub menu======end end end end"); 145 | } 146 | } 147 | return super.toString(); 148 | } 149 | 150 | @Override 151 | public ArrayList getMenuDatas() { 152 | return mItems; 153 | } 154 | 155 | } 156 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/menu/OpenMenuItem.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.menu; 2 | 3 | /** 4 | * 菜单item接口. 5 | * 6 | * @author hailongqiu 7 | */ 8 | public interface OpenMenuItem { 9 | static final int NO_ICON = 0; // 默认icon id. 10 | static final int DEFAULT_TEXT_SIZE = 24; // 默认字体 11 | 12 | /** 13 | * 设置菜单item-ID. 14 | */ 15 | public OpenMenuItem setId(int id); 16 | 17 | public int getId(); 18 | 19 | /** 20 | * 设置菜单资源. 21 | */ 22 | public OpenMenuItem setIconRes(int iconID); 23 | public int getIconRes(); 24 | 25 | /** 26 | * 设置菜单文本内容 27 | */ 28 | public OpenMenuItem setTitle(CharSequence title); 29 | 30 | public CharSequence getTitle(); 31 | 32 | /** 33 | * 设置字体大小. 34 | */ 35 | public OpenMenuItem setTextSize(int size); 36 | 37 | public int getTextSize(); 38 | 39 | /** 40 | * 设置数据. 41 | */ 42 | public OpenMenuItem setObjectData(Object data); 43 | 44 | public Object getObjectData(); 45 | 46 | /** 47 | * 添加子菜单. 48 | */ 49 | public OpenMenuItem addSubMenu(OpenMenu openSubMenu); 50 | 51 | public OpenMenu getSubMenu(); 52 | 53 | public OpenMenuItem showSubMenu(); 54 | public OpenMenuItem hideSubMenu(); 55 | 56 | /** 57 | * 判断子菜单是否存在. 58 | */ 59 | public boolean hasSubMenu(); 60 | 61 | /** 62 | * 判断子菜单是否显示了. 63 | */ 64 | public boolean isShowSubMenu(); 65 | public void setShowSubMenu(boolean isShow); 66 | 67 | /** 68 | * 设置checked标志位. 69 | */ 70 | public OpenMenuItem setChecked(boolean checked); 71 | 72 | public boolean isChecked(); 73 | 74 | public OpenMenu getMenu(); 75 | public void setMenu(OpenMenu menu); 76 | 77 | /** 78 | * 菜单item接口函数. 79 | */ 80 | public interface ItemView { 81 | public void initialize(OpenMenuItem itemData); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/menu/OpenMenuItemImpl.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.menu; 2 | 3 | public class OpenMenuItemImpl implements OpenMenuItem { 4 | 5 | private int mIconID; // icon 资源 id. 6 | private int mId; // item id. 7 | private int mTextSize = DEFAULT_TEXT_SIZE; // item 文本大小. 8 | private boolean mChecked = false; // item 勾选状态. 9 | private boolean mIsShowSubMenu = false; // 是否显示子菜单. 10 | private Object mData; 11 | private CharSequence mTitle; // item 文本. 12 | 13 | private OpenMenu mMenu; // 保存 item的父菜单. 14 | private OpenMenu mSubMenu; // 保存 子菜单. 15 | 16 | 17 | OpenMenuItemImpl(OpenMenu menu, int id, CharSequence title) { 18 | this.mMenu = menu; 19 | this.mId = id; 20 | this.mTitle = title; 21 | } 22 | 23 | @Override 24 | public OpenMenuItem showSubMenu() { 25 | if (mSubMenu != null) { 26 | mSubMenu.showMenu(); 27 | } 28 | this.mIsShowSubMenu = true; 29 | return this; 30 | } 31 | 32 | @Override 33 | public OpenMenuItem hideSubMenu() { 34 | if (mSubMenu != null) { 35 | mSubMenu.hideMenu(); 36 | } 37 | this.mIsShowSubMenu = false; 38 | return this; 39 | } 40 | 41 | /** 42 | * 设置 图标资源id. 43 | */ 44 | @Override 45 | public OpenMenuItem setIconRes(int iconID) { 46 | this.mIconID = iconID; 47 | notifyChanged(); 48 | return this; 49 | } 50 | 51 | @Override 52 | public int getIconRes() { 53 | return this.mIconID; 54 | } 55 | 56 | @Override 57 | public OpenMenuItem setTitle(CharSequence title) { 58 | this.mTitle = title; 59 | notifyChanged(); 60 | return this; 61 | } 62 | 63 | @Override 64 | public CharSequence getTitle() { 65 | return mTitle; 66 | } 67 | 68 | /** 69 | * 添加子菜单. 70 | */ 71 | @Override 72 | public OpenMenuItem addSubMenu(OpenMenu openSubMenu) { 73 | this.mSubMenu = openSubMenu; 74 | this.mSubMenu.setParentMenu(mMenu); // 添加父菜单. 75 | return this; 76 | } 77 | 78 | @Override 79 | public OpenMenu getSubMenu() { 80 | return mSubMenu; 81 | } 82 | 83 | @Override 84 | public boolean hasSubMenu() { 85 | return (mSubMenu != null); 86 | } 87 | 88 | @Override 89 | public boolean isShowSubMenu() { 90 | return this.mIsShowSubMenu; 91 | } 92 | 93 | @Override 94 | public void setShowSubMenu(boolean isShow) { 95 | this.mIsShowSubMenu = isShow; 96 | } 97 | 98 | @Override 99 | public OpenMenuItem setChecked(boolean checked) { 100 | this.mChecked = checked; 101 | notifyChanged(); 102 | return this; 103 | } 104 | 105 | @Override 106 | public boolean isChecked() { 107 | return this.mChecked; 108 | } 109 | 110 | @Override 111 | public OpenMenuItem setTextSize(int size) { 112 | mTextSize = size; 113 | notifyChanged(); 114 | return this; 115 | } 116 | 117 | @Override 118 | public int getTextSize() { 119 | return mTextSize; 120 | } 121 | 122 | @Override 123 | public OpenMenuItem setObjectData(Object data) { 124 | this.mData = data; 125 | notifyChanged(); 126 | return this; 127 | } 128 | 129 | @Override 130 | public Object getObjectData() { 131 | return this.mData; 132 | } 133 | 134 | @Override 135 | public OpenMenuItem setId(int id) { 136 | this.mId = id; 137 | return this; 138 | } 139 | 140 | @Override 141 | public int getId() { 142 | return this.mId; 143 | } 144 | 145 | /** 146 | * 数据更新. 147 | */ 148 | 149 | private void notifyChanged() { 150 | if (mMenu != null) { 151 | mMenu.notifyChanged(); 152 | } 153 | } 154 | 155 | public OpenMenu getMenu() { 156 | return this.mMenu; 157 | } 158 | 159 | @Override 160 | public void setMenu(OpenMenu menu) { 161 | this.mMenu = menu; 162 | } 163 | 164 | } 165 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/menu/OpenMenuItemView.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.menu; 2 | 3 | import android.content.Context; 4 | import android.graphics.drawable.Drawable; 5 | import android.util.AttributeSet; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.CompoundButton; 10 | import android.widget.ImageView; 11 | import android.widget.LinearLayout; 12 | import android.widget.RadioButton; 13 | import android.widget.TextView; 14 | 15 | import com.open.androidtvwidget.R; 16 | import com.open.androidtvwidget.utils.GenerateViewId; 17 | import com.open.androidtvwidget.utils.OPENLOG; 18 | 19 | /** 20 | * Created by hailongqiu on 2016/8/23. 21 | */ 22 | public class OpenMenuItemView extends LinearLayout implements OpenMenuItem.ItemView { 23 | private OpenMenuItem mItemData; 24 | private Context mContext; 25 | 26 | private ImageView mIconView; 27 | private CompoundButton mCompoundButton; 28 | private TextView mTitleView; 29 | 30 | private LayoutInflater mInflater; 31 | 32 | public OpenMenuItemView(Context context) { 33 | this(context, null, 0); 34 | } 35 | 36 | public OpenMenuItemView(Context context, AttributeSet attrs) { 37 | this(context, attrs, 0); 38 | } 39 | 40 | public OpenMenuItemView(Context context, AttributeSet attrs, int defStyleAttr) { 41 | super(context, attrs, defStyleAttr); 42 | this.mContext = context; 43 | // 44 | setFocusableInTouchMode(true); 45 | setFocusable(true); 46 | } 47 | 48 | @Override 49 | protected void onFinishInflate() { 50 | super.onFinishInflate(); 51 | mTitleView = (TextView) findViewById(R.id.title_tv); 52 | } 53 | 54 | @Override 55 | public void initialize(OpenMenuItem itemData) { 56 | this.mItemData = itemData; 57 | // 58 | setTitle(itemData.getTitle()); 59 | setIcon(itemData.getIconRes()); 60 | setTextSize(itemData.getTextSize()); 61 | setChecked(itemData, itemData.isChecked()); 62 | // 如果没有设置菜单ITEM ID,则默认设置ID. 63 | if (itemData.getId() == 0) { 64 | int id = GenerateViewId.getSingleton().generateViewId(); 65 | itemData.setId(id); 66 | } 67 | setId(itemData.getId()); // item ID. 68 | } 69 | 70 | public void setTitle(CharSequence title) { 71 | if (title != null) { 72 | mTitleView.setText(title); 73 | if (mTitleView.getVisibility() != VISIBLE) 74 | mTitleView.setVisibility(VISIBLE); 75 | } else { 76 | if (mTitleView.getVisibility() != GONE) 77 | mTitleView.setVisibility(GONE); 78 | } 79 | } 80 | 81 | public void setIcon(int icon) { 82 | if (mIconView == null && icon <= 0) 83 | return; 84 | 85 | if (mIconView == null) { 86 | insertIconView(); 87 | } 88 | 89 | if (icon > 0) { 90 | Drawable d = getContext().getResources().getDrawable(icon); 91 | mIconView.setImageDrawable(d); 92 | if (mIconView.getVisibility() != VISIBLE) { 93 | mIconView.setVisibility(VISIBLE); 94 | } 95 | } else { 96 | mIconView.setVisibility(GONE); 97 | } 98 | } 99 | 100 | public void setChecked(OpenMenuItem itemData, boolean checked) { 101 | // if (itemData.getCheckedView() != null) { 102 | // if (mCompoundButton == null) { 103 | // insertCompoundButton(itemData); 104 | // } 105 | // mCompoundButton.setChecked(checked); 106 | // } 107 | } 108 | 109 | /** 110 | * 设置字体. 111 | */ 112 | public void setTextSize(int size) { 113 | if (size > 0) { 114 | mTitleView.setTextSize(size); 115 | } 116 | } 117 | 118 | private void insertIconView() { 119 | LayoutInflater inflater = getInflater(); 120 | mIconView = (ImageView) inflater.inflate(R.layout.list_menu_item_icon, this, false); 121 | addView(mIconView, 0); 122 | } 123 | 124 | /** 125 | * 插入勾选view. 126 | */ 127 | private void insertCompoundButton(OpenMenuItem itemData) { 128 | mCompoundButton = new RadioButton(mContext); 129 | addView(mCompoundButton, 0); 130 | } 131 | 132 | private LayoutInflater getInflater() { 133 | if (mInflater == null) { 134 | mInflater = LayoutInflater.from(getContext()); 135 | } 136 | return mInflater; 137 | } 138 | 139 | @Override 140 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 141 | if (mIconView != null) { 142 | ViewGroup.LayoutParams lp = getLayoutParams(); 143 | LayoutParams iconLp = (LayoutParams) mIconView.getLayoutParams(); 144 | if (lp.height > 0 && iconLp.width <= 0) { 145 | iconLp.width = lp.height; 146 | } 147 | } 148 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 149 | } 150 | 151 | } 152 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/utils/AnimateUtils.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.utils; 2 | 3 | import android.view.animation.Animation; 4 | import android.view.animation.ScaleAnimation; 5 | 6 | /** 7 | * 动画集合. 8 | * 9 | * @author hailongqiu 356752238@qq.com 10 | * 11 | */ 12 | public class AnimateUtils { 13 | public static Animation zoomAnimation(float startScale, float endScale, long duration) { 14 | ScaleAnimation anim = new ScaleAnimation(startScale, endScale, startScale, endScale, Animation.RELATIVE_TO_SELF, 15 | 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); 16 | anim.setFillAfter(true); 17 | anim.setDuration(duration); 18 | return anim; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/utils/DisplayUtil.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.utils; 2 | 3 | import android.content.Context; 4 | 5 | public class DisplayUtil { 6 | public static int dip2px(Context context, float dipValue) { 7 | final float scale = context.getResources().getDisplayMetrics().density; 8 | return (int) (dipValue * scale + 0.5f); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/utils/DrawUtils.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.utils; 2 | 3 | import android.graphics.Path; 4 | import android.graphics.RectF; 5 | 6 | public class DrawUtils { 7 | 8 | public static Path addRoundPath3(int width, int height, float radius) { 9 | Path path = new Path(); 10 | path.addRoundRect(new RectF(0, 0, width, height), radius, radius, Path.Direction.CW); 11 | return path; 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/utils/GenerateViewId.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.utils; 2 | 3 | import java.util.concurrent.atomic.AtomicInteger; 4 | 5 | import android.annotation.SuppressLint; 6 | import android.os.Build; 7 | import android.view.View; 8 | 9 | public class GenerateViewId { 10 | private static final AtomicInteger sNextGeneratedId = new AtomicInteger(1); 11 | private static GenerateViewId sGenerateViewId = new GenerateViewId(); 12 | 13 | public static GenerateViewId getSingleton() { 14 | return sGenerateViewId; 15 | } 16 | 17 | /** 18 | * 动态生成View ID API LEVEL 17 以上View.generateViewId()生成 API LEVEL 17 以下需要手动生成 19 | */ 20 | @SuppressLint("NewApi") 21 | public int generateViewId() { 22 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) { 23 | for (;;) { 24 | final int result = sNextGeneratedId.get(); 25 | // aapt-generated IDs have the high byte nonzero; clamp to the 26 | // range under that. 27 | int newValue = result + 1; 28 | if (newValue > 0x00FFFFFF) 29 | newValue = 1; // Roll over to 1, not 0. 30 | if (sNextGeneratedId.compareAndSet(result, newValue)) { 31 | return result; 32 | } 33 | } 34 | } else { 35 | return View.generateViewId(); 36 | } 37 | 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/utils/NetWorkUtils.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.utils; 2 | 3 | import android.content.Context; 4 | import android.net.ConnectivityManager; 5 | import android.net.NetworkInfo; 6 | 7 | /** 8 | * 一些网络常用的接口.
9 | * 记得要添加权限哈.
10 | * 11 | * 12 | * @author hailongqiu 13 | * 14 | */ 15 | public class NetWorkUtils { 16 | 17 | /** 18 | * 网络是否可用 19 | */ 20 | public static boolean isNetWorkdetect(Context context) { 21 | ConnectivityManager conn = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 22 | NetworkInfo networkinfo = conn.getActiveNetworkInfo(); 23 | if (networkinfo == null || !networkinfo.isAvailable()) { 24 | return false; 25 | } 26 | return true; 27 | } 28 | 29 | /** 30 | * 判断有线网络. 31 | */ 32 | public static boolean checkEthernet(Context context) { 33 | ConnectivityManager conn = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 34 | NetworkInfo networkInfo = conn.getNetworkInfo(ConnectivityManager.TYPE_ETHERNET); 35 | return networkInfo.isConnected(); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/utils/OPENLOG.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.utils; 2 | 3 | import android.text.TextUtils; 4 | import android.util.Log; 5 | 6 | /** 7 | * 使用: 8 | * OPENLOG.initTag("hailongqiu", true); // 测试LOG输出. 9 | * 如果不进行初始化,那么将使用命令来开启LOG输出. 10 | * 开启LOG输出: 11 | *

12 | * setprop log.tag.$getTag() DEBUG 13 | *

14 | * 15 | * @author hailongqiu 16 | */ 17 | public class OPENLOG { 18 | 19 | private static String sTag = ""; 20 | private static boolean sDebug = false; 21 | 22 | /** 23 | * 初始化tag信息. 24 | */ 25 | public static void initTag(String tag) { 26 | initTag(tag, false); 27 | } 28 | 29 | /** 30 | * 初始化设置调试位. 31 | */ 32 | public static void initTag(boolean debug) { 33 | initTag(sTag, debug); 34 | } 35 | 36 | public static void initTag(String tag, boolean debug) { 37 | sTag = tag; 38 | sDebug = debug; 39 | } 40 | 41 | public static void D(String str, Object... args) { 42 | if (isDebug()) { 43 | Log.d(getTag(), buildLogString(str, args)); 44 | } 45 | } 46 | 47 | public static void V(String str, Object... args) { 48 | if (isDebug()) { 49 | Log.v(getTag(), buildLogString(str, args)); 50 | } 51 | } 52 | 53 | public static void E(String str, Object... args) { 54 | if (isDebug()) { 55 | Log.d(getTag(), buildLogString(str, args)); 56 | } 57 | } 58 | 59 | /** 60 | * 如果sTAG是空则自动从StackTrace中取TAG 61 | */ 62 | private static String getTag() { 63 | if (!TextUtils.isEmpty(sTag)) { 64 | return sTag; 65 | } 66 | StackTraceElement caller = new Throwable().fillInStackTrace().getStackTrace()[2]; 67 | return caller.getFileName(); 68 | } 69 | 70 | private static String buildLogString(String str, Object... args) { 71 | if (args.length > 0) { 72 | str = String.format(str, args); 73 | } 74 | // 75 | StackTraceElement caller = new Throwable().fillInStackTrace().getStackTrace()[2]; 76 | StringBuilder stringBuilder = new StringBuilder(); 77 | // 78 | // if (TextUtils.isEmpty(sTag)) { 79 | // stringBuilder.append(caller.getMethodName()) 80 | // .append("():") 81 | // .append(caller.getLineNumber()) 82 | // .append(":") 83 | // .append(str); 84 | // } else { 85 | stringBuilder 86 | .append("(") 87 | .append(caller.getFileName()) 88 | .append(":") 89 | .append(caller.getLineNumber()) 90 | .append(").") 91 | .append(caller.getMethodName()) 92 | .append("():") 93 | .append(str); 94 | // } 95 | return stringBuilder.toString(); 96 | } 97 | 98 | private static boolean isDebug() { 99 | return sDebug || Log.isLoggable(getTag(), Log.DEBUG); 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/utils/Utils.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.utils; 2 | 3 | /** 4 | * 5 | * @author hailongqiu 356752238@qq.com 6 | * 7 | */ 8 | public class Utils { 9 | 10 | /** 11 | * 获取SDK版本 12 | */ 13 | public static int getSDKVersion() { 14 | int version = 0; 15 | try { 16 | version = Integer.valueOf(android.os.Build.VERSION.SDK); 17 | } catch (NumberFormatException e) { 18 | } 19 | return version; 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/view/ExpendGridView.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.view; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.widget.GridView; 6 | 7 | /** 8 | * 扩展Gridview.必需嵌套滚动窗口. 9 | * @author hailongqiu 10 | * 11 | */ 12 | public class ExpendGridView extends GridView { 13 | public ExpendGridView(Context context, AttributeSet attrs, int defStyle) { 14 | super(context, attrs, defStyle); 15 | } 16 | 17 | public ExpendGridView(Context context, AttributeSet attrs) { 18 | super(context, attrs); 19 | } 20 | 21 | @Override 22 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 23 | int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, 24 | MeasureSpec.AT_MOST); 25 | super.onMeasure(widthMeasureSpec, expandSpec); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/view/FrameMainLayout.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.view; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.view.View; 6 | import android.widget.FrameLayout; 7 | 8 | /** 9 | * 如果有控件放大被挡住,可以使用 MainLayout, 10 | *

11 | * 它继承于 FrameLayout. 12 | *

13 | * 使用方式就和FrameLayout是一样的 14 | *

15 | * 如果你其它的控件也被挡住了,但是这里没有,你可以使用WidgetTvViewBring 16 | *

17 | * 18 | * @author hailongqiu 19 | * 20 | */ 21 | public class FrameMainLayout extends FrameLayout { 22 | 23 | public FrameMainLayout(Context context) { 24 | super(context); 25 | init(context); 26 | } 27 | 28 | public FrameMainLayout(Context context, AttributeSet attrs) { 29 | super(context, attrs); 30 | init(context); 31 | } 32 | 33 | WidgetTvViewBring mWidgetTvViewBring; 34 | 35 | private void init(Context context) { 36 | this.setChildrenDrawingOrderEnabled(true); 37 | mWidgetTvViewBring = new WidgetTvViewBring(this); 38 | } 39 | 40 | @Override 41 | public void bringChildToFront(View child) { 42 | mWidgetTvViewBring.bringChildToFront(this, child); 43 | } 44 | 45 | @Override 46 | protected int getChildDrawingOrder(int childCount, int i) { 47 | return mWidgetTvViewBring.getChildDrawingOrder(childCount, i); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/view/GridViewTV.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.view; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.widget.GridView; 6 | 7 | /** 8 | * GridView TV版本. 9 | * 10 | * @author hailongqiu 356752238@qq.com 11 | */ 12 | public class GridViewTV extends GridView { 13 | 14 | public GridViewTV(Context context) { 15 | this(context, null); 16 | } 17 | 18 | public GridViewTV(Context context, AttributeSet attrs) { 19 | super(context, attrs); 20 | init(context, attrs); 21 | } 22 | 23 | @Override 24 | public boolean isInTouchMode() { 25 | return !(hasFocus() && !super.isInTouchMode()); 26 | } 27 | 28 | private void init(Context context, AttributeSet attrs) { 29 | this.setChildrenDrawingOrderEnabled(true); 30 | } 31 | 32 | @Override 33 | protected int getChildDrawingOrder(int childCount, int i) { 34 | if (this.getSelectedItemPosition() != -1) { 35 | if (i + this.getFirstVisiblePosition() == this.getSelectedItemPosition()) {// 这是原本要在最后一个刷新的item 36 | return childCount - 1; 37 | } 38 | if (i == childCount - 1) {// 这是最后一个需要刷新的item 39 | return this.getSelectedItemPosition() - this.getFirstVisiblePosition(); 40 | } 41 | } 42 | return i; 43 | } 44 | 45 | public void setDefualtSelect(int pos) { 46 | requestFocusFromTouch(); 47 | setSelection(pos); 48 | } 49 | 50 | } -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/view/LinearMainLayout.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.view; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.view.View; 6 | import android.widget.LinearLayout; 7 | 8 | /** 9 | * 如果有控件放大被挡住,可以使用 LinearMainLayout,

10 | * 它继承于 LinearLayout.

11 | * 使用方式和LinerLayout是一样的

12 | * @author hailongqiu 13 | * 14 | */ 15 | public class LinearMainLayout extends LinearLayout { 16 | 17 | public LinearMainLayout(Context context) { 18 | super(context); 19 | init(context); 20 | } 21 | 22 | public LinearMainLayout(Context context, AttributeSet attrs) { 23 | super(context, attrs); 24 | init(context); 25 | } 26 | 27 | WidgetTvViewBring mWidgetTvViewBring; 28 | 29 | private void init(Context context) { 30 | this.setChildrenDrawingOrderEnabled(true); 31 | mWidgetTvViewBring = new WidgetTvViewBring(this); 32 | } 33 | 34 | @Override 35 | public void bringChildToFront(View child) { 36 | mWidgetTvViewBring.bringChildToFront(this, child); 37 | } 38 | 39 | @Override 40 | protected int getChildDrawingOrder(int childCount, int i) { 41 | return mWidgetTvViewBring.getChildDrawingOrder(childCount, i); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/view/ListViewTV.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.view; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.util.AttributeSet; 6 | import android.view.View; 7 | import android.widget.ListView; 8 | 9 | public class ListViewTV extends ListView { 10 | 11 | public ListViewTV(Context context) { 12 | this(context, null); 13 | } 14 | 15 | public ListViewTV(Context context, AttributeSet attrs) { 16 | super(context, attrs); 17 | init(context, attrs); 18 | } 19 | 20 | /** 21 | * 崩溃了. 22 | */ 23 | @Override 24 | protected void dispatchDraw(Canvas canvas) { 25 | try { 26 | super.dispatchDraw(canvas); 27 | } catch (Exception e) { 28 | e.printStackTrace(); 29 | } 30 | } 31 | 32 | @Override 33 | public boolean isInTouchMode() { 34 | return !(hasFocus() && !super.isInTouchMode()); 35 | } 36 | 37 | WidgetTvViewBring mWidgetTvViewBring; 38 | 39 | private void init(Context context, AttributeSet attrs) { 40 | this.setChildrenDrawingOrderEnabled(true); 41 | mWidgetTvViewBring = new WidgetTvViewBring(this); 42 | } 43 | 44 | @Override 45 | public void bringChildToFront(View child) { 46 | mWidgetTvViewBring.bringChildToFront(this, child); 47 | } 48 | 49 | @Override 50 | protected int getChildDrawingOrder(int childCount, int i) { 51 | return mWidgetTvViewBring.getChildDrawingOrder(childCount, i); 52 | } 53 | 54 | public void setDefualtSelect(int pos) { 55 | requestFocusFromTouch(); 56 | setSelection(pos); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/view/OpenTabHost.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.view; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import com.open.androidtvwidget.R; 7 | import com.open.androidtvwidget.adapter.BaseTabTitleAdapter; 8 | 9 | import android.content.Context; 10 | import android.graphics.Color; 11 | import android.util.AttributeSet; 12 | import android.view.LayoutInflater; 13 | import android.view.View; 14 | import android.widget.TabHost; 15 | import android.widget.TabWidget; 16 | 17 | /** 18 | * 标题栏控

19 | * 你也可以不使用,或者用button,用TextView都可以,

20 | * 不过你需要处理下焦点问题.

21 | * TextView 可能要设置下 android:focusable="true"

22 | * 可以使用 android:nextFocusRight 来控制焦点.

23 | * 24 | * @author hailongqiu 25 | * 26 | */ 27 | public class OpenTabHost extends TabHost { 28 | 29 | public OpenTabHost(Context context) { 30 | this(context, null); 31 | } 32 | 33 | public OpenTabHost(Context context, AttributeSet attrs) { 34 | super(context, attrs); 35 | init(context, attrs); 36 | } 37 | 38 | private Context mContext; 39 | private TabWidget mTabWidget; // 标题栏. 40 | private BaseTabTitleAdapter mAdapter; 41 | private List mCacheView = new ArrayList(); 42 | private OnTabSelectListener mOnTabSelectListener; 43 | 44 | private void init(Context context, AttributeSet attrs) { 45 | this.mContext = context; 46 | LayoutInflater.from(context).inflate(R.layout.tabhost_title_head, this, true); 47 | mTabWidget = (TabWidget) findViewById(android.R.id.tabs); 48 | setup(); 49 | mTabWidget.setStripEnabled(false); 50 | initViewEvents(); 51 | this.setBackgroundColor(Color.TRANSPARENT); 52 | } 53 | 54 | private void initViewEvents() { 55 | setOnTabChangedListener(new OnTabChangeListener() { 56 | @Override 57 | public void onTabChanged(String tabId) { 58 | int position = getCurrentTab(); 59 | OnTabSelectListener cb = mOnTabSelectListener; 60 | if (cb != null) { 61 | TabWidget tw = getTabWidget(); 62 | View titleWidget = tw.getChildTabViewAt(position); 63 | cb.onTabSelect(OpenTabHost.this, titleWidget, position); 64 | } 65 | } 66 | }); 67 | } 68 | 69 | public void setOnTabSelectListener(OnTabSelectListener cb) { 70 | this.mOnTabSelectListener = cb; 71 | } 72 | 73 | public OnTabSelectListener getOnTabSelectListener() { 74 | return this.mOnTabSelectListener; 75 | } 76 | 77 | public void addTabWidget(TabSpec tabSpec) { 78 | tabSpec.setContent(new DummyTabFactory(mContext)); 79 | this.addTab(tabSpec); 80 | } 81 | 82 | class DummyTabFactory implements TabContentFactory { 83 | private final Context mContext; 84 | 85 | public DummyTabFactory(Context context) { 86 | this.mContext = context; 87 | } 88 | 89 | /** 90 | * 创建一个空的Content. 91 | */ 92 | @Override 93 | public View createTabContent(String tag) { 94 | View v = new View(mContext); 95 | v.setMinimumWidth(0); 96 | v.setMinimumHeight(0); 97 | return v; 98 | } 99 | } 100 | 101 | public void setAdapter(BaseTabTitleAdapter adapter) { 102 | mCacheView.clear(); 103 | clearAllTabs(); 104 | this.mAdapter = adapter; 105 | if (this.mAdapter != null) { 106 | int count = this.mAdapter.getCount(); 107 | if (count > 0) { 108 | for (int i = 0; i < count; i++) { 109 | View titleView = this.mAdapter.getView(i, null, this); 110 | mCacheView.add(titleView); 111 | TabSpec tabSpec = this.newTabSpec(i + "").setIndicator(titleView); 112 | this.addTabWidget(tabSpec); 113 | } 114 | requestLayout(); 115 | } 116 | } 117 | } 118 | 119 | public List getAllTitleView() { 120 | return this.mCacheView; 121 | } 122 | 123 | public View getTitleViewIndexAt(int index) { 124 | return this.mCacheView.get(index); 125 | } 126 | 127 | public View getTitleViewIdAt(int index) { 128 | return this.mCacheView.get(index).findViewById(this.mAdapter.getTitleWidgetID(index)); 129 | } 130 | 131 | public interface OnTabSelectListener { 132 | public void onTabSelect(OpenTabHost openTabHost, View titleWidget, int postion); 133 | } 134 | 135 | /** 136 | * 修复鼠标点击,按数字键会丢失焦点的问题 137 | * 倾听者(344176791) 修复. 138 | */ 139 | @Override 140 | public void onTouchModeChanged(boolean isInTouchMode) { 141 | } 142 | 143 | } 144 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/view/RelativeMainLayout.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.view; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.view.View; 6 | import android.widget.RelativeLayout; 7 | 8 | /** 9 | * 如果有控件放大被挡住,可以使用 RelativeMainLayout,

10 | * 它继承于 LinearLayout.

11 | * 使用方式和LinerLayout是一样的

12 | * @author hailongqiu 13 | * 14 | */ 15 | public class RelativeMainLayout extends RelativeLayout { 16 | 17 | public RelativeMainLayout(Context context) { 18 | super(context); 19 | init(context); 20 | } 21 | 22 | public RelativeMainLayout(Context context, AttributeSet attrs) { 23 | super(context, attrs); 24 | init(context); 25 | } 26 | 27 | WidgetTvViewBring mWidgetTvViewBring; 28 | 29 | private void init(Context context) { 30 | this.setChildrenDrawingOrderEnabled(true); 31 | mWidgetTvViewBring = new WidgetTvViewBring(this); 32 | } 33 | 34 | @Override 35 | public void bringChildToFront(View child) { 36 | mWidgetTvViewBring.bringChildToFront(this, child); 37 | } 38 | 39 | @Override 40 | protected int getChildDrawingOrder(int childCount, int i) { 41 | return mWidgetTvViewBring.getChildDrawingOrder(childCount, i); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/view/SmoothHorizontalScrollView.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.view; 2 | 3 | import com.open.androidtvwidget.R; 4 | 5 | import android.content.Context; 6 | import android.graphics.Rect; 7 | import android.util.AttributeSet; 8 | import android.widget.HorizontalScrollView; 9 | 10 | /** 11 | * 当你使用滚动窗口焦点错乱的时候,就可以使用这个控件. 12 | *

13 | * 使用方法和滚动窗口是一样的,具体查看DEMO吧. 14 | *

15 | * 如果想改变滚动的系数,R.dimen.fading_edge 16 | *

17 | * 18 | * @author hailongqiu 19 | */ 20 | public class SmoothHorizontalScrollView extends HorizontalScrollView { 21 | final String TAG = "SmoothHorizontalScrollView"; 22 | 23 | private int mFadingEdge; 24 | 25 | public SmoothHorizontalScrollView(Context context) { 26 | this(context, null, 0); 27 | } 28 | 29 | public SmoothHorizontalScrollView(Context context, AttributeSet attrs) { 30 | this(context, attrs, 0); 31 | } 32 | 33 | public SmoothHorizontalScrollView(Context context, AttributeSet attrs, int defStyle) { 34 | super(context, attrs, defStyle); 35 | } 36 | 37 | public void setFadingEdge(int fadingEdge) { 38 | this.mFadingEdge = fadingEdge; 39 | } 40 | 41 | @Override 42 | protected int computeScrollDeltaToGetChildRectOnScreen(Rect rect) { 43 | if (getChildCount() == 0) 44 | return 0; 45 | 46 | int width = getWidth(); 47 | int screenLeft = getScrollX(); 48 | int screenRight = screenLeft + width; 49 | 50 | int fadingEdge = mFadingEdge > 0 ? mFadingEdge : this.getResources().getDimensionPixelSize(R.dimen.fading_edge); 51 | 52 | // leave room for left fading edge as long as rect isn't at very left 53 | if (rect.left > 0) { 54 | screenLeft += fadingEdge; 55 | } 56 | 57 | // leave room for right fading edge as long as rect isn't at very right 58 | if (rect.right < getChildAt(0).getWidth()) { 59 | screenRight -= fadingEdge; 60 | } 61 | 62 | int scrollXDelta = 0; 63 | 64 | if (rect.right > screenRight && rect.left > screenLeft) { 65 | // need to move right to get it in view: move right just enough so 66 | // that the entire rectangle is in view (or at least the first 67 | // screen size chunk). 68 | 69 | if (rect.width() > width) { 70 | // just enough to get screen size chunk on 71 | scrollXDelta += (rect.left - screenLeft); 72 | } else { 73 | // get entire rect at right of screen 74 | scrollXDelta += (rect.right - screenRight); 75 | } 76 | 77 | // make sure we aren't scrolling beyond the end of our content 78 | int right = getChildAt(0).getRight(); 79 | int distanceToRight = right - screenRight; 80 | scrollXDelta = Math.min(scrollXDelta, distanceToRight); 81 | 82 | } else if (rect.left < screenLeft && rect.right < screenRight) { 83 | // need to move right to get it in view: move right just enough so 84 | // that 85 | // entire rectangle is in view (or at least the first screen 86 | // size chunk of it). 87 | 88 | if (rect.width() > width) { 89 | // screen size chunk 90 | scrollXDelta -= (screenRight - rect.right); 91 | } else { 92 | // entire rect at left 93 | scrollXDelta -= (screenLeft - rect.left); 94 | } 95 | 96 | // make sure we aren't scrolling any further than the left our 97 | // content 98 | scrollXDelta = Math.max(scrollXDelta, -getScrollX()); 99 | } 100 | return scrollXDelta; 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/view/SmoothVorizontalScrollView.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.view; 2 | 3 | import com.open.androidtvwidget.R; 4 | 5 | import android.content.Context; 6 | import android.graphics.Rect; 7 | import android.util.AttributeSet; 8 | import android.widget.ScrollView; 9 | 10 | /** 11 | * 当你使用滚动窗口焦点错乱的时候,就可以使用这个控件. 12 | *

13 | * 使用方法和滚动窗口是一样的,具体查看DEMO吧. 14 | *

15 | * 如果想改变滚动的系数,R.dimen.fading_edge 16 | *

17 | * 18 | * @author hailongqiu 19 | */ 20 | public class SmoothVorizontalScrollView extends ScrollView { 21 | 22 | public SmoothVorizontalScrollView(Context context) { 23 | super(context, null, 0); 24 | } 25 | 26 | private int mFadingEdge; 27 | 28 | public SmoothVorizontalScrollView(Context context, AttributeSet attrs) { 29 | super(context, attrs, 0); 30 | } 31 | 32 | public SmoothVorizontalScrollView(Context context, AttributeSet attrs, int defStyle) { 33 | super(context, attrs, defStyle); 34 | } 35 | 36 | public void setFadingEdge(int fadingEdge) { 37 | this.mFadingEdge = fadingEdge; 38 | } 39 | 40 | @Override 41 | protected int computeScrollDeltaToGetChildRectOnScreen(Rect rect) { 42 | if (getChildCount() == 0) 43 | return 0; 44 | int height = getHeight(); 45 | int screenTop = getScrollY(); 46 | int screenBottom = screenTop + height; 47 | int fadingEdge = mFadingEdge > 0 ? mFadingEdge : this.getResources().getDimensionPixelSize(R.dimen.fading_edge); 48 | if (rect.top > 0) { 49 | screenTop += fadingEdge; 50 | } 51 | if (rect.bottom < getChildAt(0).getHeight()) { 52 | screenBottom -= fadingEdge; 53 | } 54 | // 55 | int scrollYDelta = 0; 56 | if (rect.bottom > screenBottom && rect.top > screenTop) { 57 | if (rect.height() > height) { 58 | scrollYDelta += (rect.top - screenTop); 59 | } else { 60 | scrollYDelta += (rect.bottom - screenBottom); 61 | } 62 | int bottom = getChildAt(0).getBottom(); 63 | int distanceToBottom = bottom - screenBottom; 64 | scrollYDelta = Math.min(scrollYDelta, distanceToBottom); 65 | } else if (rect.top < screenTop && rect.bottom < screenBottom) { 66 | if (rect.height() > height) { 67 | scrollYDelta -= (screenBottom - rect.bottom); 68 | } else { 69 | scrollYDelta -= (screenTop - rect.top); 70 | } 71 | scrollYDelta = Math.max(scrollYDelta, -getScrollY()); 72 | } 73 | return scrollYDelta; 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/view/TextViewWithTTF.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.view; 2 | 3 | 4 | import java.util.HashMap; 5 | 6 | import com.open.androidtvwidget.R; 7 | 8 | import android.content.Context; 9 | import android.content.res.TypedArray; 10 | import android.graphics.Typeface; 11 | import android.util.AttributeSet; 12 | import android.widget.TextView; 13 | 14 | /** 15 | * 一个自定义的TextView控件. 16 | * 加载 /system/font/** 下面的字库. 17 | * @author hailongqiu 18 | * 19 | */ 20 | public class TextViewWithTTF extends TextView { 21 | 22 | private static final TypeFaceMgr SFontMgr = new TypeFaceMgr(); 23 | 24 | public TextViewWithTTF(Context context) { 25 | super(context); 26 | } 27 | 28 | public TextViewWithTTF(Context context, AttributeSet attrs) { 29 | super(context, attrs); 30 | 31 | TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.TextViewWithTTF); 32 | setTypeface(SFontMgr.getTypeface(array.getString(R.styleable.TextViewWithTTF_ttf_name))); 33 | array.recycle(); 34 | } 35 | 36 | 37 | 38 | public TextViewWithTTF(Context context, String aTTFName) 39 | { 40 | super(context); 41 | setTypeface(SFontMgr.getTypeface(aTTFName)); 42 | } 43 | 44 | public void setFont(String aTTFName) { 45 | setTypeface(SFontMgr.getTypeface(aTTFName)); 46 | } 47 | } 48 | 49 | 50 | class TypeFaceMgr 51 | { 52 | HashMap mTypefaces; 53 | public TypeFaceMgr() 54 | { 55 | mTypefaces = new HashMap(); 56 | } 57 | public Typeface getTypeface(String aTTFName) 58 | { 59 | if(mTypefaces.containsKey(aTTFName)) 60 | { 61 | return mTypefaces.get(aTTFName); 62 | } 63 | else 64 | { 65 | Typeface font; 66 | try 67 | { 68 | font = Typeface.createFromFile("/system/fonts/"+aTTFName); 69 | } 70 | catch (Exception e) 71 | { 72 | e.printStackTrace(); 73 | return null; 74 | } 75 | 76 | mTypefaces.put(aTTFName, font); 77 | return font; 78 | } 79 | } 80 | } -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/view/ViewPagerTV.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.view; 2 | 3 | import android.content.Context; 4 | import android.support.v4.view.ViewPager; 5 | import android.util.AttributeSet; 6 | 7 | public class ViewPagerTV extends ViewPager { 8 | 9 | public ViewPagerTV(Context context) { 10 | super(context); 11 | } 12 | 13 | public ViewPagerTV(Context context, AttributeSet attrs) { 14 | super(context, attrs); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/java/com/open/androidtvwidget/view/WidgetTvViewBring.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget.view; 2 | 3 | import android.view.View; 4 | import android.view.ViewGroup; 5 | 6 | /** 7 | * 让ViewGroup中的控件在最上层
8 | * ListView, GridView, ViewGroup中使用. 想要置顶的子控件,不要忘记调用 bringToFront() 噢. 9 | * 10 | * @author hailongqiu 11 | * 12 | */ 13 | public class WidgetTvViewBring { 14 | 15 | private int position = 0; 16 | 17 | public WidgetTvViewBring() { 18 | } 19 | 20 | public WidgetTvViewBring(ViewGroup vg) { 21 | vg.setClipChildren(false); 22 | vg.setClipToPadding(false); 23 | // vg.setChildrenDrawingOrderEnabled(true); 24 | } 25 | 26 | public void bringChildToFront(ViewGroup vg, View child) { 27 | position = vg.indexOfChild(child); 28 | if (position != -1) { 29 | vg.postInvalidate(); 30 | } 31 | } 32 | 33 | /** 34 | * 此函数 dispatchDraw 中调用.
35 | * 原理就是和最后一个要绘制的view,交换了位置.
36 | * 因为dispatchDraw最后一个绘制的view是在最上层的.
37 | * 这样就避免了使用 bringToFront 导致焦点错乱问题.
38 | */ 39 | public int getChildDrawingOrder(int childCount, int i) { 40 | if (position != -1) { 41 | if (i == childCount - 1) 42 | return position; 43 | if (i == position) 44 | return childCount - 1; 45 | } 46 | return i; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/res/drawable/nocolor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonFengIce/AndroidTVWidget/e38677cdef5605e69cba0e558d305e997a5174d2/Demo/androidtvwidget/src/main/res/drawable/nocolor.png -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/res/layout/lb_content_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/res/layout/lb_item_container.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/res/layout/list_menu_item_icon.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/res/layout/list_menu_item_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 11 | 12 | 13 | 14 | 26 | 27 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/res/layout/open_menu_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/res/layout/softkey_layout_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | 16 | 17 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/res/layout/tabhost_title_head.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 11 | 12 | 17 | 18 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 80dp 6 | 7 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | AndroidTvWidget 3 | 4 | -------------------------------------------------------------------------------- /Demo/androidtvwidget/src/test/java/com/open/androidtvwidget/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.open.androidtvwidget; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * To work on unit tests, switch the Test Artifact in the Build Variants view. 9 | */ 10 | public class ExampleUnitTest { 11 | @Test 12 | public void addition_isCorrect() throws Exception { 13 | assertEquals(4, 2 + 2); 14 | } 15 | } -------------------------------------------------------------------------------- /Demo/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /Demo/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion rootProject.ext.androidCompileSdkVersion 5 | buildToolsVersion rootProject.ext.androidBuildToolsVersion 6 | 7 | defaultConfig { 8 | minSdkVersion rootProject.ext.androidMinSdkVersion 9 | targetSdkVersion rootProject.ext.androidTargetSdkVersion 10 | applicationId "com.open.demo" 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | Map dependencies = rootProject.ext.dependencies 24 | compile fileTree(include: ['*.jar'], dir: 'libs') 25 | testCompile 'junit:junit:4.12' 26 | compile project(':androidtvwidget') 27 | compile dependencies.appCompat 28 | } 29 | -------------------------------------------------------------------------------- /Demo/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in F:\dev\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /Demo/app/src/androidTest/java/com/open/demo/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.open.demo; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /Demo/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 14 | 15 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /Demo/app/src/main/java/com/open/demo/DemoListViewActivity.java: -------------------------------------------------------------------------------- 1 | package com.open.demo; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import com.open.androidtvwidget.bridge.EffectNoDrawBridge; 7 | import com.open.androidtvwidget.utils.Utils; 8 | import com.open.androidtvwidget.view.ListViewTV; 9 | import com.open.androidtvwidget.view.MainUpView; 10 | 11 | import android.app.Activity; 12 | import android.graphics.Rect; 13 | import android.os.Bundle; 14 | import android.os.Handler; 15 | import android.os.Message; 16 | import android.view.LayoutInflater; 17 | import android.view.View; 18 | import android.view.ViewGroup; 19 | import android.widget.AdapterView; 20 | import android.widget.AdapterView.OnItemClickListener; 21 | import android.widget.AdapterView.OnItemSelectedListener; 22 | import android.widget.BaseAdapter; 23 | import android.widget.ListView; 24 | import android.widget.TextView; 25 | import android.widget.Toast; 26 | 27 | public class DemoListViewActivity extends Activity { 28 | 29 | private static final String TAG = "DemoListViewActivity"; 30 | 31 | private List data; 32 | private MainUpView mainUpView1; 33 | private LayoutInflater mInflater; 34 | private View mOldView; 35 | private ListViewTV listView; 36 | 37 | @Override 38 | protected void onCreate(Bundle savedInstanceState) { 39 | super.onCreate(savedInstanceState); 40 | setContentView(R.layout.demo_list_view); 41 | this.mInflater = LayoutInflater.from(getApplicationContext()); 42 | listView = (ListViewTV) findViewById(R.id.listview); 43 | mainUpView1 = (MainUpView) findViewById(R.id.mainUpView1); 44 | // 默认是 OpenEff...,建议使用 NoDraw... ... 45 | mainUpView1.setEffectBridge(new EffectNoDrawBridge()); 46 | EffectNoDrawBridge bridget = (EffectNoDrawBridge) mainUpView1.getEffectBridge(); 47 | bridget.setTranDurAnimTime(200); 48 | // 49 | mainUpView1.setUpRectResource(R.drawable.white_light_10); // 设置移动边框的图片. 50 | mainUpView1.setDrawUpRectPadding(new Rect(25, 25, 23, 23)); // 边框图片设置间距. 51 | // 52 | initData(); 53 | // 54 | listView.setAdapter(new DemoAdapter()); 55 | // 56 | listView.setOnItemSelectedListener(new OnItemSelectedListener() { 57 | @Override 58 | public void onItemSelected(AdapterView parent, View view, int position, long id) { 59 | if (view != null) { 60 | view.bringToFront(); 61 | mainUpView1.setFocusView(view, mOldView, 1.2f); 62 | mOldView = view; 63 | } 64 | } 65 | 66 | @Override 67 | public void onNothingSelected(AdapterView parent) { 68 | } 69 | }); 70 | listView.setOnItemClickListener(new OnItemClickListener() { 71 | @Override 72 | public void onItemClick(AdapterView parent, View view, int position, long id) { 73 | Toast.makeText(getApplicationContext(), "position : " + position, Toast.LENGTH_LONG).show(); 74 | } 75 | }); 76 | // 延时请求其它位置的item. 77 | Handler handler = new Handler() { 78 | @Override 79 | public void handleMessage(Message msg) { 80 | listView.setDefualtSelect(2); 81 | } 82 | }; 83 | handler.sendMessageDelayed(handler.obtainMessage(), 188); 84 | } 85 | 86 | public void initData() { 87 | data = new ArrayList(); 88 | for (int i = 0; i < 105; i++) { 89 | String text = "item" + i; 90 | data.add(text); 91 | } 92 | } 93 | 94 | public class DemoAdapter extends BaseAdapter { 95 | 96 | @Override 97 | public int getCount() { 98 | return data.size(); 99 | } 100 | 101 | @Override 102 | public Object getItem(int position) { 103 | return data.get(position); 104 | } 105 | 106 | @Override 107 | public long getItemId(int position) { 108 | return position; 109 | } 110 | 111 | @Override 112 | public View getView(int position, View convertView, ViewGroup parent) { 113 | ViewHolder holder = null; 114 | if (convertView == null) { 115 | holder = new ViewHolder(); 116 | convertView = mInflater.inflate(R.layout.item_listview, null); 117 | holder.title = (TextView) convertView.findViewById(R.id.textView); 118 | convertView.setTag(holder); 119 | } else { 120 | holder = (ViewHolder) convertView.getTag(); 121 | } 122 | holder.title.setText(data.get(position)); 123 | return convertView; 124 | } 125 | 126 | public class ViewHolder { 127 | public TextView title; 128 | } 129 | } 130 | 131 | } 132 | -------------------------------------------------------------------------------- /Demo/app/src/main/java/com/open/demo/DemoMenuActivity.java: -------------------------------------------------------------------------------- 1 | package com.open.demo; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.graphics.drawable.Drawable; 6 | import android.os.Bundle; 7 | import android.support.v7.widget.DefaultItemAnimator; 8 | import android.support.v7.widget.LinearLayoutManager; 9 | import android.view.View; 10 | import android.view.View.OnClickListener; 11 | 12 | import com.open.androidtvwidget.bridge.OpenEffectBridge; 13 | import com.open.androidtvwidget.menu.OpenMenu; 14 | import com.open.androidtvwidget.menu.OpenMenuImpl; 15 | import com.open.androidtvwidget.menu.OpenMenuItem; 16 | import com.open.androidtvwidget.leanback.recycle.RecyclerViewTV; 17 | import com.open.androidtvwidget.utils.OPENLOG; 18 | import com.open.androidtvwidget.view.MainUpView; 19 | import com.open.androidtvwidget.leanback.adapter.GeneralAdapter; 20 | import com.open.demo.menu.TreeMenuPresenter; 21 | 22 | /** 23 | * 菜单DEMO测试. 24 | * 25 | * @author hailongqiu 26 | */ 27 | public class DemoMenuActivity extends Activity implements OnClickListener { 28 | 29 | private Context mContext; 30 | OpenMenu mOpenMenu; 31 | RecyclerViewTV mRecyclerView; 32 | 33 | public DemoMenuActivity() { 34 | OPENLOG.initTag("hailongqiu", true); // 测试LOG输出. 35 | } 36 | 37 | @Override 38 | protected void onCreate(Bundle savedInstanceState) { 39 | super.onCreate(savedInstanceState); 40 | setContentView(R.layout.demo_menu_activity); 41 | // findViewById(R.id.content11).setBackgroundResource(R.drawable.main_bg); 42 | findViewById(R.id.button1).setOnClickListener(this); 43 | findViewById(R.id.button2).setOnClickListener(this); 44 | mContext = DemoMenuActivity.this; 45 | initAllMenu(); 46 | } 47 | 48 | private Drawable getResources(int id) { 49 | return getResources().getDrawable(id); 50 | } 51 | 52 | private void initAllMenu() { 53 | // 主菜单. 54 | mOpenMenu = new OpenMenuImpl(); 55 | final OpenMenuItem menuItem1 = mOpenMenu.add("菜单1"); 56 | menuItem1.setIconRes(R.drawable.ic_launcher).setId(R.id.menu_1_1).setChecked(true); 57 | mOpenMenu.add("菜单2").setIconRes(R.drawable.ic_launcher).setId(R.id.menu_1_2); 58 | mOpenMenu.add("菜单3").setIconRes(R.drawable.ic_launcher).setId(R.id.menu_1_3); 59 | mOpenMenu.add("菜单4").setIconRes(R.drawable.ic_launcher).setId(R.id.menu_1_4); 60 | mOpenMenu.add("菜单5").setIconRes(R.drawable.ic_launcher).setId(R.id.menu_1_5); 61 | mOpenMenu.add("菜单6").setIconRes(R.drawable.ic_launcher); 62 | mOpenMenu.add("菜单7").setIconRes(R.drawable.ic_launcher); 63 | // 菜单1的子菜单. 64 | OpenMenu subMenu1 = new OpenMenuImpl(); 65 | subMenu1.add("菜单1-1"); 66 | subMenu1.add("菜单1-2").setIconRes(R.drawable.ic_launcher); 67 | subMenu1.add("菜单1-3"); 68 | // 菜单2的子菜单. 69 | OpenMenu subMenu2 = new OpenMenuImpl(); 70 | subMenu2.add("菜单5-1"); 71 | subMenu2.add("菜单5-2"); 72 | subMenu2.add("菜单5-3"); 73 | // 菜单1-2 添加子菜单(第三级菜单). 74 | OpenMenu subMenu1_2 = new OpenMenuImpl(); 75 | subMenu1_2.add("菜单1-2-1"); 76 | subMenu1_2.add("菜单1-2-2"); 77 | subMenu1_2.add("菜单1-2-3"); 78 | // 79 | OpenMenu subMenu1_2_1 = new OpenMenuImpl(); 80 | subMenu1_2_1.add("菜单1-2-1-1"); 81 | subMenu1_2_1.add("菜单1-2-1-2"); 82 | subMenu1_2.addSubMenu(0, subMenu1_2_1); // 三级菜单添加四级菜单. 83 | // 添加子菜单. 84 | menuItem1.addSubMenu(subMenu1); // 一级菜单的menuItem1 添加二级菜单. 85 | mOpenMenu.addSubMenu(4, subMenu2); // 一级菜单添加二级菜单. 86 | subMenu1.addSubMenu(1, subMenu1_2); // 二级菜单添加三级菜单. 87 | // 输出菜单数据. 88 | mOpenMenu.toString(); 89 | // test menu. 90 | final MainUpView mainUpView = new MainUpView(mContext); 91 | mainUpView.setEffectBridge(new OpenEffectBridge()); 92 | mainUpView.setUpRectResource(R.drawable.white_light_10); 93 | mRecyclerView = (RecyclerViewTV) findViewById(R.id.recyclerView_menu); 94 | GeneralAdapter menuAdapter = new GeneralAdapter(new TreeMenuPresenter(mRecyclerView, mOpenMenu)); 95 | mRecyclerView.setLayoutManager(new LinearLayoutManager(mContext)); 96 | mRecyclerView.setAdapter(menuAdapter); 97 | mRecyclerView.setItemAnimator(new DefaultItemAnimator()); 98 | mRecyclerView.setOnItemListener(new RecyclerViewTV.OnItemListener() { 99 | @Override 100 | public void onItemPreSelected(RecyclerViewTV parent, View itemView, int position) { 101 | mainUpView.setUnFocusView(itemView); 102 | } 103 | @Override 104 | public void onItemSelected(RecyclerViewTV parent, View itemView, int position) { 105 | mainUpView.setFocusView(itemView, 1.0f); 106 | } 107 | @Override 108 | public void onReviseFocusFollow(RecyclerViewTV parent, View itemView, int position) { 109 | } 110 | }); 111 | } 112 | 113 | @Override 114 | public void onClick(View v) { 115 | int id = v.getId(); 116 | switch (id) { 117 | case R.id.button1: 118 | mOpenMenu.showMenu(); 119 | break; 120 | case R.id.button2: 121 | mOpenMenu.hideMenu(); 122 | break; 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /Demo/app/src/main/java/com/open/demo/DemoViewflipperActivity.java: -------------------------------------------------------------------------------- 1 | package com.open.demo; 2 | 3 | import android.app.Activity; 4 | import android.graphics.Rect; 5 | import android.os.Bundle; 6 | import android.os.Handler; 7 | import android.os.Message; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.view.ViewTreeObserver; 11 | import android.widget.Button; 12 | 13 | import com.open.androidtvwidget.utils.OPENLOG; 14 | import com.open.androidtvwidget.view.MainUpView; 15 | import com.open.androidtvwidget.view.SmoothVorizontalScrollView; 16 | 17 | /** 18 | */ 19 | public class DemoViewflipperActivity extends Activity { 20 | 21 | View view1; 22 | View view2; 23 | View view3; 24 | 25 | Button switch1_btn; 26 | SmoothVorizontalScrollView vf; 27 | 28 | @Override 29 | protected void onCreate(Bundle savedInstanceState) { 30 | super.onCreate(savedInstanceState); 31 | setContentView(R.layout.activity_viewflipper_demo); 32 | vf = (SmoothVorizontalScrollView) findViewById(R.id.vscrollview); 33 | vf.setFadingEdge(200); 34 | OPENLOG.initTag("hailongqiu", true); 35 | // 36 | switch1_btn = (Button) findViewById(R.id.switch1_btn); 37 | 38 | view1 = findViewById(R.id.content11); 39 | view2 = findViewById(R.id.content12); 40 | view3 = findViewById(R.id.content13); 41 | 42 | final MainUpView mainUpView = (MainUpView) findViewById(R.id.mainUpView1); 43 | mainUpView.setUpRectResource(R.drawable.test_rectangle); 44 | vf.getViewTreeObserver().addOnGlobalFocusChangeListener(new ViewTreeObserver.OnGlobalFocusChangeListener() { 45 | @Override 46 | public void onGlobalFocusChanged(View oldFocus, View newFocus) { 47 | mainUpView.setFocusView(newFocus, oldFocus, 1.0f); 48 | } 49 | }); 50 | // 翻页. 51 | switch1_btn.setOnClickListener(new View.OnClickListener() { 52 | @Override 53 | public void onClick(View v) { 54 | ViewGroup viewGroup = (ViewGroup) vf.getChildAt(0); 55 | View view = viewGroup.getChildAt(1); 56 | OPENLOG.D("view:" + view); 57 | if (view != null) { 58 | Rect rect = findLocationWithView(view); 59 | int scrollY = vf.getScrollY(); 60 | int offset = rect.top + scrollY; 61 | if (offset < 0) { 62 | offset = 0; 63 | } 64 | vf.scrollTo(0, offset); 65 | } 66 | } 67 | }); 68 | } 69 | 70 | public Rect findLocationWithView(View view) { 71 | ViewGroup root = (ViewGroup) vf.getParent(); 72 | Rect rect = new Rect(); 73 | root.offsetDescendantRectToMyCoords(view, rect); 74 | return rect; 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /Demo/app/src/main/java/com/open/demo/MenuAnimationUtils.java: -------------------------------------------------------------------------------- 1 | package com.open.demo; 2 | 3 | import android.content.Context; 4 | import android.view.animation.AlphaAnimation; 5 | import android.view.animation.Animation; 6 | import android.view.animation.AnimationSet; 7 | import android.view.animation.LayoutAnimationController; 8 | import android.view.animation.RotateAnimation; 9 | import android.view.animation.TranslateAnimation; 10 | 11 | /** 12 | * Created by hailongqiu on 2016/6/3. 13 | */ 14 | public class MenuAnimationUtils { 15 | 16 | /** 17 | * 从左到右显示菜单. 18 | */ 19 | public static Animation showAnimation() { 20 | Animation animation = new TranslateAnimation(Animation.RELATIVE_TO_SELF, -1.0f, Animation.RELATIVE_TO_SELF, 21 | 0.0f, Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, 0.0f); 22 | animation.setDuration(1000); 23 | return animation; 24 | } 25 | 26 | /** 27 | * 从右到左隐藏菜单. 28 | */ 29 | public static Animation hideAnimation() { 30 | Animation animation = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, 31 | -1.0f, Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, 0.0f); 32 | animation.setDuration(500); 33 | return animation; 34 | } 35 | 36 | /** 37 | * 加载动画. 38 | */ 39 | @SuppressWarnings("ResourceType") 40 | public static LayoutAnimationController loadAnimation(Context context) { 41 | /* 42 | * 创建动画的集合 43 | */ 44 | AnimationSet set = new AnimationSet(false); 45 | Animation animation; 46 | /* 47 | * 创建旋转动画 48 | */ 49 | animation = new RotateAnimation(180, 10); 50 | animation.setDuration(1000); 51 | set.addAnimation(animation); 52 | 53 | LayoutAnimationController controller = new LayoutAnimationController(set, 1); 54 | controller.setInterpolator(context, android.R.anim.accelerate_interpolator); 55 | controller.setAnimation(set); 56 | return controller; 57 | } 58 | 59 | /** 60 | * 加载动画2. 61 | */ 62 | public static LayoutAnimationController loadAnimation2() { 63 | int duration = 300; 64 | AnimationSet set = new AnimationSet(true); 65 | 66 | Animation animation = new AlphaAnimation(0.0f, 1.0f); 67 | animation.setDuration(duration); 68 | set.addAnimation(animation); 69 | 70 | animation = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, 0.0f, 71 | Animation.RELATIVE_TO_SELF, -1.0f, Animation.RELATIVE_TO_SELF, 0.0f); 72 | animation.setDuration(duration); 73 | set.addAnimation(animation); 74 | 75 | LayoutAnimationController controller = new LayoutAnimationController(set, 0.5f); 76 | controller.setOrder(LayoutAnimationController.ORDER_NORMAL); 77 | return controller; 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /Demo/app/src/main/java/com/open/demo/NewViewPager.java: -------------------------------------------------------------------------------- 1 | package com.open.demo; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.util.Log; 6 | import android.view.View; 7 | import android.widget.ScrollView; 8 | 9 | import com.open.androidtvwidget.utils.OPENLOG; 10 | import com.open.androidtvwidget.view.SmoothVorizontalScrollView; 11 | 12 | import java.util.ArrayList; 13 | 14 | /** 15 | * Created by hailongqiu on 2016/11/29. 16 | */ 17 | 18 | public class NewViewPager extends SmoothVorizontalScrollView { 19 | public NewViewPager(Context context) { 20 | super(context); 21 | } 22 | 23 | public NewViewPager(Context context, AttributeSet attrs) { 24 | super(context, attrs); 25 | } 26 | 27 | public NewViewPager(Context context, AttributeSet attrs, int defStyleAttr) { 28 | super(context, attrs, defStyleAttr); 29 | } 30 | 31 | @Override 32 | public boolean dispatchUnhandledMove(View focused, int direction) { 33 | OPENLOG.E("direction:" + direction); 34 | if (direction == View.FOCUS_DOWN) { 35 | OPENLOG.E("direction FOCUS_DOWN:" + direction); 36 | } else if (direction == View.FOCUS_UP) { 37 | OPENLOG.E("direction FOCUS_UP:" + direction); 38 | } 39 | return super.dispatchUnhandledMove(focused, direction); 40 | } 41 | 42 | @Override 43 | public void computeScroll() { 44 | super.computeScroll(); 45 | } 46 | 47 | @Override 48 | public void addFocusables(ArrayList views, int direction, int focusableMode) { 49 | OPENLOG.E("direction:" + direction + " focusableMode:" + focusableMode); 50 | super.addFocusables(views, direction, focusableMode); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Demo/app/src/main/java/com/open/demo/Rotate3dAnimation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.open.demo; 18 | 19 | import android.view.animation.Animation; 20 | import android.view.animation.Transformation; 21 | import android.graphics.Camera; 22 | import android.graphics.Matrix; 23 | 24 | /** 25 | * 一个在指定了两个角度的在Y轴旋转的动画类. 26 | * 这个类也添加了一个z轴的属性用来提高效果. 27 | * An animation that rotates the view on the Y axis between two specified angles. 28 | * This animation also adds a translation on the Z axis (depth) to improve the effect. 29 | */ 30 | public class Rotate3dAnimation extends Animation { 31 | private final float mFromDegrees; 32 | private final float mToDegrees; 33 | private final float mCenterX; 34 | private final float mCenterY; 35 | private final float mDepthZ; 36 | private final boolean mReverse; 37 | private Camera mCamera; 38 | 39 | /** 40 | * 在Y轴创建了一个新的3D的旋转动画,这个旋转动画定义了它的开始角度和结束角度, 41 | * 两个角度的单位都是度数,这个旋转动画围绕在2D空间的中心点执行. 42 | * 你可以用X轴坐标(叫做centerX)和Y轴(叫做centerY)坐标来定义这个中心点. 43 | * 当动画开始时,对于z轴(深度)的转换就会被执行.转换的长度和转换正向反向都可以指定. 44 | * Creates a new 3D rotation on the Y axis. The rotation is defined by its 45 | * start angle and its end angle. Both angles are in degrees. The rotation 46 | * is performed around a center point on the 2D space, definied by a pair 47 | * of X and Y coordinates, called centerX and centerY. When the animation 48 | * starts, a translation on the Z axis (depth) is performed. The length 49 | * of the translation can be specified, as well as whether the translation 50 | * should be reversed in time. 51 | * 52 | * @param fromDegrees the start angle of the 3D rotation 开始的角度 53 | * @param toDegrees the end angle of the 3D rotation 结束的角度 54 | * @param centerX the X center of the 3D rotation 中心点X轴坐标 55 | * @param centerY the Y center of the 3D rotation 中心点Y轴坐标 56 | * @param reverse true if the translation should be reversed, false otherwise true表示反向,false表示正向 57 | */ 58 | public Rotate3dAnimation(float fromDegrees, float toDegrees, 59 | float centerX, float centerY, float depthZ, boolean reverse) { 60 | mFromDegrees = fromDegrees; 61 | mToDegrees = toDegrees; 62 | mCenterX = centerX; 63 | mCenterY = centerY; 64 | mDepthZ = depthZ; 65 | mReverse = reverse; 66 | } 67 | 68 | @Override 69 | public void initialize(int width, int height, int parentWidth, int parentHeight) { 70 | super.initialize(width, height, parentWidth, parentHeight); 71 | mCamera = new Camera(); 72 | } 73 | 74 | @Override 75 | protected void applyTransformation(float interpolatedTime, Transformation t) { 76 | final float fromDegrees = mFromDegrees; 77 | float degrees = fromDegrees + ((mToDegrees - fromDegrees) * interpolatedTime); 78 | 79 | final float centerX = mCenterX; 80 | final float centerY = mCenterY; 81 | final Camera camera = mCamera; 82 | 83 | final Matrix matrix = t.getMatrix(); 84 | 85 | camera.save(); 86 | if (mReverse) { 87 | camera.translate(0.0f, 0.0f, mDepthZ * interpolatedTime); 88 | } else { 89 | camera.translate(0.0f, 0.0f, mDepthZ * (1.0f - interpolatedTime)); 90 | } 91 | camera.rotateY(degrees); 92 | camera.getMatrix(matrix); 93 | camera.restore(); 94 | 95 | matrix.preTranslate(-centerX, -centerY); 96 | matrix.postTranslate(centerX, centerY); 97 | } 98 | } -------------------------------------------------------------------------------- /Demo/app/src/main/java/com/open/demo/adapter/GridViewHolder.java: -------------------------------------------------------------------------------- 1 | package com.open.demo.adapter; 2 | 3 | import com.open.androidtvwidget.leanback.mode.OpenPresenter; 4 | import com.open.demo.R; 5 | 6 | import android.view.View; 7 | import android.widget.ImageView; 8 | import android.widget.TextView; 9 | 10 | public class GridViewHolder extends OpenPresenter.ViewHolder { 11 | 12 | public ImageView iv; 13 | public TextView tv; 14 | public TextView head_tv; 15 | 16 | public GridViewHolder(View itemView) { 17 | super(itemView); 18 | // iv = (ImageView)itemView.findViewById(R.id.); 19 | tv = (TextView)itemView.findViewById(R.id.textView); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /Demo/app/src/main/java/com/open/demo/adapter/LeftMenuPresenter.java: -------------------------------------------------------------------------------- 1 | package com.open.demo.adapter; 2 | 3 | import android.view.ViewGroup; 4 | import android.widget.Button; 5 | 6 | import com.open.androidtvwidget.leanback.mode.OpenPresenter; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | /** 12 | * RecyclerView demo 的左侧菜单. 13 | * Created by hailongqiu on 2016/8/24. 14 | */ 15 | public class LeftMenuPresenter extends OpenPresenter { 16 | 17 | private List strList = new ArrayList() { 18 | { 19 | add("横向LinerLayout"); 20 | add("纵向LinerLayout"); 21 | add("横向GridLayout"); 22 | add("纵向GridLayout"); 23 | add("Leanback demo"); 24 | } 25 | }; 26 | 27 | @Override 28 | public int getItemCount() { 29 | return strList.size(); 30 | } 31 | 32 | @Override 33 | public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 34 | Button btn = new Button(parent.getContext()); 35 | return new ViewHolder(btn); 36 | } 37 | 38 | @Override 39 | public void onBindViewHolder(ViewHolder viewHolder, int position) { 40 | Button btn = (Button) viewHolder.view; 41 | btn.setFocusableInTouchMode(true); // 只是测试鼠标效果. 42 | btn.setFocusable(true); 43 | String str = strList.get(position); 44 | btn.setText(str); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /Demo/app/src/main/java/com/open/demo/adapter/OpenTabTitleAdapter.java: -------------------------------------------------------------------------------- 1 | package com.open.demo.adapter; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import com.open.androidtvwidget.adapter.BaseTabTitleAdapter; 7 | import com.open.androidtvwidget.view.TextViewWithTTF; 8 | import com.open.demo.R; 9 | 10 | import android.content.Context; 11 | import android.content.res.Resources; 12 | import android.graphics.Typeface; 13 | import android.view.View; 14 | import android.view.ViewGroup; 15 | import android.widget.LinearLayout; 16 | 17 | public class OpenTabTitleAdapter extends BaseTabTitleAdapter { 18 | private List titleList = new ArrayList(); 19 | 20 | public OpenTabTitleAdapter() { 21 | titleList.add("推荐"); 22 | titleList.add("电影"); 23 | titleList.add("电视剧"); 24 | titleList.add("设置"); 25 | } 26 | 27 | @Override 28 | public int getCount() { 29 | return titleList.size(); 30 | } 31 | 32 | /** 33 | * 为何要设置ID标识。
34 | * 因为PAGE页面中的ITEM如果向上移到标题栏,
35 | * 它会查找最近的,你只需要在布局中设置
36 | * android:nextFocusUp="@+id/title_bar1"
37 | * 就可以解决焦点问题哦. 38 | */ 39 | private List ids = new ArrayList() { 40 | { 41 | add(R.id.title_bar1); 42 | add(R.id.title_bar2); 43 | add(R.id.title_bar3); 44 | add(R.id.title_bar4); 45 | } 46 | }; 47 | 48 | @Override 49 | public Integer getTitleWidgetID(int pos) { 50 | return ids.get(pos); 51 | } 52 | 53 | @Override 54 | public View getView(int position, View convertView, ViewGroup parent) { 55 | parent.getContext(); 56 | String title = titleList.get(position); 57 | if (convertView == null) { 58 | convertView = newTabIndicator(parent.getContext(), title, false); 59 | convertView.setId(ids.get(position)); // 设置ID. 60 | } else { 61 | // ... ... 62 | } 63 | return convertView; 64 | } 65 | 66 | /** 67 | * 这里只是demo,你可以设置自己的标题栏. 68 | */ 69 | private View newTabIndicator(Context context, String tabName, boolean focused) { 70 | final String name = tabName; 71 | View viewC = View.inflate(context, R.layout.tab_view_indicator_item, null); 72 | TextViewWithTTF view = (TextViewWithTTF) viewC.findViewById(R.id.tv_tab_indicator); 73 | LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 74 | ViewGroup.LayoutParams.WRAP_CONTENT); 75 | lp.setMargins(20, 0, 20, 0); 76 | view.setLayoutParams(lp); 77 | 78 | // mTabWidget.setPadding(getResources().getDimensionPixelSize(R.dimen.tab_left_offset), 79 | // 0, 0, 0); 80 | 81 | view.setText(name); 82 | 83 | if (focused == true) { 84 | Resources res = context.getResources(); 85 | view.setTextColor(res.getColor(android.R.color.white)); 86 | view.setTypeface(null, Typeface.BOLD); 87 | view.requestFocus(); 88 | } 89 | return viewC; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /Demo/app/src/main/java/com/open/demo/adapter/RecyclerViewPresenter.java: -------------------------------------------------------------------------------- 1 | package com.open.demo.adapter; 2 | 3 | import android.view.LayoutInflater; 4 | import android.view.View; 5 | import android.view.ViewGroup; 6 | import android.widget.TextView; 7 | 8 | import com.open.androidtvwidget.leanback.adapter.GeneralAdapter; 9 | import com.open.androidtvwidget.leanback.mode.OpenPresenter; 10 | import com.open.demo.R; 11 | 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | 15 | /** 16 | * 测试. 17 | * Created by hailongqiu on 2016/8/24. 18 | */ 19 | public class RecyclerViewPresenter extends OpenPresenter { 20 | 21 | private final List labels; 22 | private GeneralAdapter mAdapter; 23 | 24 | public RecyclerViewPresenter(int count) { 25 | this.labels = new ArrayList(count); 26 | for (int i = 0; i < count; i++) { 27 | labels.add(String.valueOf(i)); 28 | } 29 | } 30 | 31 | @Override 32 | public void setAdapter(GeneralAdapter adapter) { 33 | this.mAdapter = adapter; 34 | } 35 | 36 | /** 37 | * 用于数据加载更多测试. 38 | */ 39 | public void addDatas(int count) { 40 | int sum = labels.size(); 41 | for (int i = sum; i < sum + count; i++) { 42 | labels.add(String.valueOf(i)); 43 | } 44 | this.mAdapter.notifyDataSetChanged(); 45 | } 46 | 47 | @Override 48 | public int getItemCount() { 49 | return labels.size(); 50 | } 51 | 52 | @Override 53 | public int getItemViewType(int position) { 54 | return 0; 55 | } 56 | 57 | @Override 58 | public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 59 | View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_recyclerview_view, parent, false); 60 | return new GridViewHolder(view); 61 | } 62 | 63 | @Override 64 | public void onBindViewHolder(ViewHolder viewHolder, int position) { 65 | GridViewHolder gridViewHolder = (GridViewHolder) viewHolder; 66 | TextView textView = (TextView) gridViewHolder.tv; 67 | textView.setText("item " + labels.get(position)); 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /Demo/app/src/main/java/com/open/demo/menu/TreeMenuPresenter.java: -------------------------------------------------------------------------------- 1 | package com.open.demo.menu; 2 | 3 | import android.support.v7.widget.RecyclerView; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | 8 | import com.open.androidtvwidget.leanback.adapter.GeneralAdapter; 9 | import com.open.androidtvwidget.leanback.recycle.RecyclerViewTV; 10 | import com.open.androidtvwidget.leanback.mode.OpenPresenter; 11 | import com.open.androidtvwidget.menu.OpenMenu; 12 | import com.open.androidtvwidget.menu.OpenMenuItem; 13 | import com.open.androidtvwidget.menu.OpenMenuItemView; 14 | import com.open.demo.R; 15 | 16 | import java.util.ArrayList; 17 | import java.util.List; 18 | 19 | /** 20 | * 树级菜单的 Presenter. (mvp) 21 | * Created by hailongqiu on 2016/8/23. 22 | */ 23 | public class TreeMenuPresenter extends OpenPresenter { 24 | 25 | private RecyclerViewTV mRecyclerViewTV; 26 | private OpenMenu mOpenMenu; 27 | 28 | public TreeMenuPresenter(RecyclerViewTV recyclerViewTV, OpenMenu openMenu) { 29 | this.mRecyclerViewTV = recyclerViewTV; 30 | this.mOpenMenu = openMenu; 31 | } 32 | 33 | private OpenMenuItem getItemPosition(int position) { 34 | return mOpenMenu.getMenuDatas().get(position); 35 | } 36 | 37 | private void addAll(List list, int pos) { 38 | mOpenMenu.getMenuDatas().addAll(pos, list); 39 | getAdapter().notifyItemRangeInserted(pos, list.size()); 40 | } 41 | 42 | private void removeAll(List list, int pos) { 43 | int sizeNum = removeAllSubMenu(list); 44 | getAdapter().notifyItemRangeRemoved(pos, sizeNum); 45 | } 46 | 47 | private int removeAllSubMenu(List list) { 48 | int sizeNum = list.size(); 49 | for (OpenMenuItem menuItem : list) { 50 | if (menuItem.hasSubMenu() && menuItem.isShowSubMenu()) { 51 | menuItem.setShowSubMenu(false); 52 | List delSubItems = menuItem.getSubMenu().getMenuDatas(); 53 | sizeNum += removeAllSubMenu(delSubItems); 54 | } 55 | } 56 | mOpenMenu.getMenuDatas().removeAll(list); 57 | return sizeNum; 58 | } 59 | 60 | @Override 61 | public int getItemCount() { 62 | return mOpenMenu != null ? mOpenMenu.getMenuDatas().size() : 0; 63 | } 64 | 65 | @Override 66 | public int getItemViewType(int position) { 67 | return 0; 68 | } 69 | 70 | @Override 71 | public ViewHolder onCreateViewHolder(final ViewGroup parent, int viewType) { 72 | View rootView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_menu_item_layout, parent, false); 73 | // 74 | OpenPresenter.ViewHolder result = new ContainerViewHolder(rootView); 75 | return result; 76 | } 77 | 78 | @Override 79 | public void onBindViewHolder(ViewHolder viewHolder, int position) { 80 | ArrayList items = mOpenMenu.getMenuDatas(); 81 | OpenMenuItem menuItem = items.get(position); 82 | ContainerViewHolder holder = (ContainerViewHolder) viewHolder; 83 | OpenMenuItemView openMenuItemView = (OpenMenuItemView) holder.view; 84 | openMenuItemView.initialize(menuItem); 85 | // 子控件. 86 | if (menuItem.getMenu().getParentMenu() != null) { 87 | RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) openMenuItemView.getLayoutParams(); 88 | lp.leftMargin = menuItem.getMenu().getTreeDepth() * 45; 89 | } 90 | // item 单击处理. 91 | mRecyclerViewTV.setOnItemClickListener(new RecyclerViewTV.OnItemClickListener() { 92 | @Override 93 | public void onItemClick(RecyclerViewTV parent, View itemView, int position) { 94 | GeneralAdapter menuAdapter = getAdapter(); 95 | OpenMenuItem menuItem = getItemPosition(position); 96 | // 判断是否存在子菜单. 97 | if (menuItem.hasSubMenu()) { 98 | if (!menuItem.isShowSubMenu()) { 99 | // 显示菜单. 100 | addAll(menuItem.getSubMenu().getMenuDatas(), position + 1); 101 | mRecyclerViewTV.scrollToPosition(position + 1); 102 | } else { 103 | // 隐藏菜单. 104 | removeAll(menuItem.getSubMenu().getMenuDatas(), position + 1); 105 | mRecyclerViewTV.scrollToPosition(position + 1); 106 | } 107 | menuItem.setShowSubMenu(!menuItem.isShowSubMenu()); 108 | } 109 | } 110 | }); 111 | } 112 | 113 | @Override 114 | public void onViewAttachedToWindow(ViewHolder viewHolder) { 115 | } 116 | 117 | @Override 118 | public void onViewDetachedFromWindow(ViewHolder viewHolder) { 119 | } 120 | 121 | @Override 122 | public void onUnbindViewHolder(ViewHolder viewHolder) { 123 | } 124 | 125 | public GeneralAdapter getAdapter() { 126 | return (GeneralAdapter) mRecyclerViewTV.getAdapter(); 127 | } 128 | 129 | static class ContainerViewHolder extends OpenPresenter.ViewHolder { 130 | public ContainerViewHolder(View view) { 131 | super(view); 132 | } 133 | } 134 | 135 | } 136 | -------------------------------------------------------------------------------- /Demo/app/src/main/java/com/open/demo/mode/ItemHeaderPresenter.java: -------------------------------------------------------------------------------- 1 | package com.open.demo.mode; 2 | 3 | import android.view.LayoutInflater; 4 | import android.view.View; 5 | import android.view.ViewGroup; 6 | import android.widget.LinearLayout; 7 | 8 | import com.open.androidtvwidget.R; 9 | import com.open.androidtvwidget.leanback.mode.OpenPresenter; 10 | import com.open.androidtvwidget.menu.OpenMenuItemView; 11 | 12 | /** 13 | * 标题头的 Presenter. (test) 14 | * Created by hailongqiu on 2016/8/24. 15 | */ 16 | public class ItemHeaderPresenter extends OpenPresenter { 17 | 18 | @Override 19 | public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 20 | View headview = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_menu_item_layout, parent, false); 21 | return new ItemHeadViewHolder(headview); 22 | } 23 | 24 | @Override 25 | public void onBindViewHolder(ViewHolder viewHolder, Object item) { 26 | LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) viewHolder.view.getLayoutParams(); 27 | lp.weight = LinearLayout.LayoutParams.MATCH_PARENT; 28 | ((OpenMenuItemView) viewHolder.view).setTitle((String)item); 29 | } 30 | 31 | static class ItemHeadViewHolder extends OpenPresenter.ViewHolder { 32 | public ItemHeadViewHolder(View view) { 33 | super(view); 34 | } 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /Demo/app/src/main/java/com/open/demo/mode/LeanbackTestData.java: -------------------------------------------------------------------------------- 1 | package com.open.demo.mode; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * Leanaback的测试数据. 8 | * Created by hailongqiu on 2016/9/6. 9 | */ 10 | public class LeanbackTestData { 11 | 12 | /** 13 | * Leanback 标题头. 14 | */ 15 | public static final String MOVIE_CATEGORY[] = { 16 | "全部软件", 17 | "聊天工具", 18 | "浏览器", 19 | "游戏娱乐", 20 | "网络游戏", 21 | "杀毒安全", 22 | }; 23 | 24 | /** 25 | * Leanback 横向 数据测试. 26 | */ 27 | public static final List MOVIE_ITEMS = new ArrayList() { 28 | { 29 | add(new Movie(0, "天天2模拟器")); 30 | add(new Movie(0, "陌陌222")); 31 | add(new Movie(0, "爱奇艺222")); 32 | add(new Movie(0, "英雄2联盟2")); 33 | add(new Movie(0, "腾讯22视频")); 34 | add(new Movie(0, "QQ22音乐")); 35 | add(new Movie(0, "无敌22讯飞")); 36 | add(new Movie(0, "360浏览器")); 37 | add(new Movie(0, "美图秀秀")); 38 | add(new Movie(0, "YY语音")); 39 | add(new Movie(0, "迅雷")); 40 | add(new Movie(0, "腾讯视频")); 41 | add(new Movie(0, "酷狗阴影")); 42 | add(new Movie(0, "优酷")); 43 | add(new Movie(0, "篮球")); 44 | add(new Movie(0, "足球")); 45 | } 46 | }; 47 | 48 | public static final List MOVIE_ITEMS2 = new ArrayList() { 49 | { 50 | add(new Movie(0, "天天模拟器AAA")); 51 | add(new Movie(0, "陌陌AAA")); 52 | add(new Movie(0, "爱奇艺222AAA")); 53 | add(new Movie(0, "英雄2联盟2AA")); 54 | add(new Movie(0, "腾讯视频AA")); 55 | add(new Movie(0, "酷狗阴影AA")); 56 | add(new Movie(0, "优酷AA")); 57 | add(new Movie(0, "篮球AA")); 58 | add(new Movie(0, "足球AAA1")); 59 | add(new Movie(0, "足球AAA15")); 60 | add(new Movie(0, "足球AAA16")); 61 | } 62 | }; 63 | 64 | } 65 | -------------------------------------------------------------------------------- /Demo/app/src/main/java/com/open/demo/mode/Movie.java: -------------------------------------------------------------------------------- 1 | package com.open.demo.mode; 2 | 3 | /** 4 | * 用于Leanback 的数据测试. 5 | * Created by hailongqiu on 2016/8/25. 6 | */ 7 | public class Movie { 8 | private int mRes; 9 | private String mTitle; 10 | 11 | public Movie(int res, String title) { 12 | this.mRes = res; 13 | this.mTitle = title; 14 | } 15 | 16 | public int getRes() { 17 | return this.mRes; 18 | } 19 | 20 | public String getTitle() { 21 | return this.mTitle; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /Demo/app/src/main/java/com/open/demo/mode/OpenCardView.java: -------------------------------------------------------------------------------- 1 | package com.open.demo.mode; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.Rect; 6 | import android.graphics.drawable.Drawable; 7 | import android.util.AttributeSet; 8 | import android.widget.FrameLayout; 9 | 10 | /** 11 | * Leanback CardView 控件. 12 | * Created by hailongqiu on 2016/8/26. 13 | */ 14 | public class OpenCardView extends FrameLayout { 15 | 16 | private final Rect mShadowBounds = new Rect(); 17 | private Drawable mDrawableShadow; 18 | 19 | public OpenCardView(Context context) { 20 | this(context, null); 21 | } 22 | 23 | public OpenCardView(Context context, AttributeSet attrs) { 24 | this(context, attrs, 0); 25 | } 26 | 27 | public OpenCardView(Context context, AttributeSet attrs, int defStyleAttr) { 28 | super(context, attrs, defStyleAttr); 29 | } 30 | 31 | public void setShadowDrawable(Drawable drawable) { 32 | this.mDrawableShadow = drawable; 33 | invalidate(); 34 | } 35 | 36 | @Override 37 | protected void onDraw(Canvas canvas) { 38 | if (mDrawableShadow != null) { 39 | Rect padding = new Rect(); 40 | mDrawableShadow.getPadding(padding); 41 | mDrawableShadow.setBounds(-padding.left, -padding.top, padding.right + canvas.getWidth(), padding.bottom + canvas.getHeight()); 42 | mDrawableShadow.draw(canvas); 43 | } 44 | super.onDraw(canvas); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Demo/app/src/main/java/com/open/demo/mode/TestMoviceListPresenter.java: -------------------------------------------------------------------------------- 1 | package com.open.demo.mode; 2 | 3 | import android.content.Context; 4 | import android.graphics.drawable.Drawable; 5 | import android.support.v7.widget.RecyclerView; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.TextView; 10 | 11 | import com.open.androidtvwidget.leanback.mode.DefualtListPresenter; 12 | import com.open.demo.R; 13 | 14 | /** 15 | * Leanback 横向item demo. 16 | * 如果你想改变标题头的样式,那就写自己的吧. 17 | * Created by hailongqiu on 2016/8/25. 18 | */ 19 | public class TestMoviceListPresenter extends DefualtListPresenter { 20 | 21 | boolean mIsSelect; 22 | 23 | /** 24 | * 你可以重写这里,传入AutoGridViewLayoutManger. 25 | */ 26 | @Override 27 | public RecyclerView.LayoutManager getLayoutManger(Context context) { 28 | return super.getLayoutManger(context); 29 | } 30 | 31 | @Override 32 | public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 33 | View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.lb_h_item, parent, false); 34 | return new ViewHolder(itemView); 35 | } 36 | 37 | public void setSelect(boolean isSelect) { 38 | this.mIsSelect = isSelect; 39 | } 40 | 41 | @Override 42 | public void onBindViewHolder(final ViewHolder viewHolder, final int position) { 43 | Movie movie = ((Movie) getItem(position)); 44 | OpenCardView openCardView = (OpenCardView) viewHolder.view; 45 | openCardView.setOnFocusChangeListener(new View.OnFocusChangeListener() { 46 | @Override 47 | public void onFocusChange(View v, boolean hasFocus) { 48 | if (hasFocus) { 49 | v.animate().scaleX(1.2f).scaleY(1.2f).setDuration(300).start(); 50 | } else { 51 | v.animate().scaleX(1.0f).scaleY(1.0f).setDuration(300).start(); 52 | } 53 | } 54 | }); 55 | openCardView.setBackgroundResource(R.drawable.mainview_cloudlist); 56 | Drawable d = viewHolder.view.getResources().getDrawable(R.drawable.ic_sp_block_focus); 57 | openCardView.setShadowDrawable(d); 58 | // 59 | TextView tv = (TextView) openCardView.findViewById(R.id.title_tv); 60 | tv.setText(movie.getTitle()); 61 | // 62 | if (this.mIsSelect) { 63 | openCardView.setAlpha(0.5f); 64 | } else { 65 | openCardView.setAlpha(1.0f); 66 | } 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable-hdpi/video_cover_cursor.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonFengIce/AndroidTVWidget/e38677cdef5605e69cba0e558d305e997a5174d2/Demo/app/src/main/res/drawable-hdpi/video_cover_cursor.9.png -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable-hdpi/white_light_10.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonFengIce/AndroidTVWidget/e38677cdef5605e69cba0e558d305e997a5174d2/Demo/app/src/main/res/drawable-hdpi/white_light_10.9.png -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable-xhdpi/cd_bg_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonFengIce/AndroidTVWidget/e38677cdef5605e69cba0e558d305e997a5174d2/Demo/app/src/main/res/drawable-xhdpi/cd_bg_1.png -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable-xhdpi/cursor_shine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonFengIce/AndroidTVWidget/e38677cdef5605e69cba0e558d305e997a5174d2/Demo/app/src/main/res/drawable-xhdpi/cursor_shine.png -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable-xhdpi/delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonFengIce/AndroidTVWidget/e38677cdef5605e69cba0e558d305e997a5174d2/Demo/app/src/main/res/drawable-xhdpi/delete.png -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable-xhdpi/dianying.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonFengIce/AndroidTVWidget/e38677cdef5605e69cba0e558d305e997a5174d2/Demo/app/src/main/res/drawable-xhdpi/dianying.png -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable-xhdpi/dou_hao_e.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonFengIce/AndroidTVWidget/e38677cdef5605e69cba0e558d305e997a5174d2/Demo/app/src/main/res/drawable-xhdpi/dou_hao_e.png -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable-xhdpi/g_search_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonFengIce/AndroidTVWidget/e38677cdef5605e69cba0e558d305e997a5174d2/Demo/app/src/main/res/drawable-xhdpi/g_search_icon.png -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable-xhdpi/grid_view_item_test.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonFengIce/AndroidTVWidget/e38677cdef5605e69cba0e558d305e997a5174d2/Demo/app/src/main/res/drawable-xhdpi/grid_view_item_test.jpg -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonFengIce/AndroidTVWidget/e38677cdef5605e69cba0e558d305e997a5174d2/Demo/app/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable-xhdpi/icon_v_default.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonFengIce/AndroidTVWidget/e38677cdef5605e69cba0e558d305e997a5174d2/Demo/app/src/main/res/drawable-xhdpi/icon_v_default.jpg -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable-xhdpi/ju_hao_e.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonFengIce/AndroidTVWidget/e38677cdef5605e69cba0e558d305e997a5174d2/Demo/app/src/main/res/drawable-xhdpi/ju_hao_e.png -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable-xhdpi/key_move_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonFengIce/AndroidTVWidget/e38677cdef5605e69cba0e558d305e997a5174d2/Demo/app/src/main/res/drawable-xhdpi/key_move_left.png -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable-xhdpi/key_move_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonFengIce/AndroidTVWidget/e38677cdef5605e69cba0e558d305e997a5174d2/Demo/app/src/main/res/drawable-xhdpi/key_move_right.png -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable-xhdpi/kongge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonFengIce/AndroidTVWidget/e38677cdef5605e69cba0e558d305e997a5174d2/Demo/app/src/main/res/drawable-xhdpi/kongge.png -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable-xhdpi/main_bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonFengIce/AndroidTVWidget/e38677cdef5605e69cba0e558d305e997a5174d2/Demo/app/src/main/res/drawable-xhdpi/main_bg.jpg -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable-xhdpi/mainview_cloudlist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonFengIce/AndroidTVWidget/e38677cdef5605e69cba0e558d305e997a5174d2/Demo/app/src/main/res/drawable-xhdpi/mainview_cloudlist.png -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable-xhdpi/mainview_huodong.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonFengIce/AndroidTVWidget/e38677cdef5605e69cba0e558d305e997a5174d2/Demo/app/src/main/res/drawable-xhdpi/mainview_huodong.png -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable-xhdpi/mainview_searchsong.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonFengIce/AndroidTVWidget/e38677cdef5605e69cba0e558d305e997a5174d2/Demo/app/src/main/res/drawable-xhdpi/mainview_searchsong.png -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable-xhdpi/mainview_tuijian.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonFengIce/AndroidTVWidget/e38677cdef5605e69cba0e558d305e997a5174d2/Demo/app/src/main/res/drawable-xhdpi/mainview_tuijian.png -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable-xhdpi/mainview_type.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonFengIce/AndroidTVWidget/e38677cdef5605e69cba0e558d305e997a5174d2/Demo/app/src/main/res/drawable-xhdpi/mainview_type.png -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable-xhdpi/mine_account.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonFengIce/AndroidTVWidget/e38677cdef5605e69cba0e558d305e997a5174d2/Demo/app/src/main/res/drawable-xhdpi/mine_account.png -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable-xhdpi/mine_handler.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonFengIce/AndroidTVWidget/e38677cdef5605e69cba0e558d305e997a5174d2/Demo/app/src/main/res/drawable-xhdpi/mine_handler.png -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable-xhdpi/quit_skb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonFengIce/AndroidTVWidget/e38677cdef5605e69cba0e558d305e997a5174d2/Demo/app/src/main/res/drawable-xhdpi/quit_skb.png -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable-xhdpi/radio_btn_check_false.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonFengIce/AndroidTVWidget/e38677cdef5605e69cba0e558d305e997a5174d2/Demo/app/src/main/res/drawable-xhdpi/radio_btn_check_false.png -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable-xhdpi/radio_btn_check_true.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonFengIce/AndroidTVWidget/e38677cdef5605e69cba0e558d305e997a5174d2/Demo/app/src/main/res/drawable-xhdpi/radio_btn_check_true.png -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable-xhdpi/softkey_bg_press2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonFengIce/AndroidTVWidget/e38677cdef5605e69cba0e558d305e997a5174d2/Demo/app/src/main/res/drawable-xhdpi/softkey_bg_press2.png -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable-xhdpi/softkey_bg_select2.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonFengIce/AndroidTVWidget/e38677cdef5605e69cba0e558d305e997a5174d2/Demo/app/src/main/res/drawable-xhdpi/softkey_bg_select2.9.png -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable-xhdpi/t9_key0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonFengIce/AndroidTVWidget/e38677cdef5605e69cba0e558d305e997a5174d2/Demo/app/src/main/res/drawable-xhdpi/t9_key0.png -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable-xhdpi/t9_key1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonFengIce/AndroidTVWidget/e38677cdef5605e69cba0e558d305e997a5174d2/Demo/app/src/main/res/drawable-xhdpi/t9_key1.png -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable-xhdpi/t9_key2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonFengIce/AndroidTVWidget/e38677cdef5605e69cba0e558d305e997a5174d2/Demo/app/src/main/res/drawable-xhdpi/t9_key2.png -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable-xhdpi/t9_key3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonFengIce/AndroidTVWidget/e38677cdef5605e69cba0e558d305e997a5174d2/Demo/app/src/main/res/drawable-xhdpi/t9_key3.png -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable-xhdpi/t9_key4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonFengIce/AndroidTVWidget/e38677cdef5605e69cba0e558d305e997a5174d2/Demo/app/src/main/res/drawable-xhdpi/t9_key4.png -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable-xhdpi/t9_key5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonFengIce/AndroidTVWidget/e38677cdef5605e69cba0e558d305e997a5174d2/Demo/app/src/main/res/drawable-xhdpi/t9_key5.png -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable-xhdpi/t9_key6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonFengIce/AndroidTVWidget/e38677cdef5605e69cba0e558d305e997a5174d2/Demo/app/src/main/res/drawable-xhdpi/t9_key6.png -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable-xhdpi/t9_key7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonFengIce/AndroidTVWidget/e38677cdef5605e69cba0e558d305e997a5174d2/Demo/app/src/main/res/drawable-xhdpi/t9_key7.png -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable-xhdpi/t9_key8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonFengIce/AndroidTVWidget/e38677cdef5605e69cba0e558d305e997a5174d2/Demo/app/src/main/res/drawable-xhdpi/t9_key8.png -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable-xhdpi/t9_key9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonFengIce/AndroidTVWidget/e38677cdef5605e69cba0e558d305e997a5174d2/Demo/app/src/main/res/drawable-xhdpi/t9_key9.png -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable-xhdpi/t9_key_clear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonFengIce/AndroidTVWidget/e38677cdef5605e69cba0e558d305e997a5174d2/Demo/app/src/main/res/drawable-xhdpi/t9_key_clear.png -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable-xhdpi/top_test_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonFengIce/AndroidTVWidget/e38677cdef5605e69cba0e558d305e997a5174d2/Demo/app/src/main/res/drawable-xhdpi/top_test_bg.png -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable-xhdpi/xiaoxie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonFengIce/AndroidTVWidget/e38677cdef5605e69cba0e558d305e997a5174d2/Demo/app/src/main/res/drawable-xhdpi/xiaoxie.png -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable-xhdpi/zongyi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonFengIce/AndroidTVWidget/e38677cdef5605e69cba0e558d305e997a5174d2/Demo/app/src/main/res/drawable-xhdpi/zongyi.png -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable/ic_bg_menu.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonFengIce/AndroidTVWidget/e38677cdef5605e69cba0e558d305e997a5174d2/Demo/app/src/main/res/drawable/ic_bg_menu.9.png -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable/ic_sp_block_focus.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonFengIce/AndroidTVWidget/e38677cdef5605e69cba0e558d305e997a5174d2/Demo/app/src/main/res/drawable/ic_sp_block_focus.9.png -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable/item_shadow.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonFengIce/AndroidTVWidget/e38677cdef5605e69cba0e558d305e997a5174d2/Demo/app/src/main/res/drawable/item_shadow.9.png -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable/key_bg_rectangle.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable/tab_title_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /Demo/app/src/main/res/drawable/test_rectangle.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 9 | -------------------------------------------------------------------------------- /Demo/app/src/main/res/layout/activity_viewflipper_demo.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 13 | 14 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 30 |