From de596deddc811e4da6f63c92d723172796c26b6f Mon Sep 17 00:00:00 2001 From: kshivang Date: Sun, 16 Jul 2017 03:01:11 +0530 Subject: [PATCH 1/2] Gradle updated 2.3.3 print library updated and java code converted to kotlin --- .gitignore | 4 +- app/build.gradle | 6 +- .../b/atv/sample/activity/MainActivity.java | 68 - .../b/atv/sample/activity/MainActivity.kt | 52 + .../activity/SingleFragmentActivity.java | 28 - .../sample/activity/SingleFragmentActivity.kt | 30 + .../fragment/CustomViewHolderFragment.java | 80 -- .../fragment/CustomViewHolderFragment.kt | 75 ++ .../fragment/FolderStructureFragment.java | 136 -- .../fragment/FolderStructureFragment.kt | 121 ++ .../fragment/SelectableTreeFragment.java | 120 -- .../sample/fragment/SelectableTreeFragment.kt | 107 ++ .../TwoDScrollingArrowExpandFragment.java | 82 -- .../TwoDScrollingArrowExpandFragment.kt | 83 ++ .../fragment/TwoDScrollingFragment.java | 70 -- .../sample/fragment/TwoDScrollingFragment.kt | 72 ++ .../ArrowExpandSelectableHeaderHolder.java | 74 -- .../ArrowExpandSelectableHeaderHolder.kt | 57 + .../b/atv/sample/holder/HeaderHolder.java | 47 - .../b/atv/sample/holder/HeaderHolder.kt | 41 + .../atv/sample/holder/IconTreeItemHolder.java | 72 -- .../b/atv/sample/holder/IconTreeItemHolder.kt | 50 + .../atv/sample/holder/PlaceHolderHolder.java | 55 - .../b/atv/sample/holder/PlaceHolderHolder.kt | 38 + .../b/atv/sample/holder/ProfileHolder.java | 43 - .../b/atv/sample/holder/ProfileHolder.kt | 36 + .../sample/holder/SelectableHeaderHolder.java | 67 - .../sample/holder/SelectableHeaderHolder.kt | 55 + .../sample/holder/SelectableItemHolder.java | 55 - .../atv/sample/holder/SelectableItemHolder.kt | 42 + .../b/atv/sample/holder/SocialViewHolder.java | 60 - .../b/atv/sample/holder/SocialViewHolder.kt | 47 + build.gradle | 4 +- gradle.properties | 6 +- gradle/wrapper/gradle-wrapper.properties | 4 +- library/build.gradle | 4 +- .../b/atv/holder/SimpleViewHolder.java | 29 - .../unnamed/b/atv/holder/SimpleViewHolder.kt | 24 + .../com/unnamed/b/atv/model/TreeNode.java | 284 ----- .../java/com/unnamed/b/atv/model/TreeNode.kt | 241 ++++ .../unnamed/b/atv/view/AndroidTreeView.java | 488 -------- .../com/unnamed/b/atv/view/AndroidTreeView.kt | 480 +++++++ .../b/atv/view/TreeNodeWrapperView.java | 52 - .../unnamed/b/atv/view/TreeNodeWrapperView.kt | 47 + .../unnamed/b/atv/view/TwoDScrollView.java | 1110 ----------------- .../com/unnamed/b/atv/view/TwoDScrollView.kt | 1089 ++++++++++++++++ 46 files changed, 2805 insertions(+), 3030 deletions(-) delete mode 100644 app/src/main/java/com/unnamed/b/atv/sample/activity/MainActivity.java create mode 100644 app/src/main/java/com/unnamed/b/atv/sample/activity/MainActivity.kt delete mode 100644 app/src/main/java/com/unnamed/b/atv/sample/activity/SingleFragmentActivity.java create mode 100644 app/src/main/java/com/unnamed/b/atv/sample/activity/SingleFragmentActivity.kt delete mode 100644 app/src/main/java/com/unnamed/b/atv/sample/fragment/CustomViewHolderFragment.java create mode 100644 app/src/main/java/com/unnamed/b/atv/sample/fragment/CustomViewHolderFragment.kt delete mode 100644 app/src/main/java/com/unnamed/b/atv/sample/fragment/FolderStructureFragment.java create mode 100644 app/src/main/java/com/unnamed/b/atv/sample/fragment/FolderStructureFragment.kt delete mode 100644 app/src/main/java/com/unnamed/b/atv/sample/fragment/SelectableTreeFragment.java create mode 100644 app/src/main/java/com/unnamed/b/atv/sample/fragment/SelectableTreeFragment.kt delete mode 100644 app/src/main/java/com/unnamed/b/atv/sample/fragment/TwoDScrollingArrowExpandFragment.java create mode 100644 app/src/main/java/com/unnamed/b/atv/sample/fragment/TwoDScrollingArrowExpandFragment.kt delete mode 100644 app/src/main/java/com/unnamed/b/atv/sample/fragment/TwoDScrollingFragment.java create mode 100644 app/src/main/java/com/unnamed/b/atv/sample/fragment/TwoDScrollingFragment.kt delete mode 100644 app/src/main/java/com/unnamed/b/atv/sample/holder/ArrowExpandSelectableHeaderHolder.java create mode 100644 app/src/main/java/com/unnamed/b/atv/sample/holder/ArrowExpandSelectableHeaderHolder.kt delete mode 100644 app/src/main/java/com/unnamed/b/atv/sample/holder/HeaderHolder.java create mode 100644 app/src/main/java/com/unnamed/b/atv/sample/holder/HeaderHolder.kt delete mode 100644 app/src/main/java/com/unnamed/b/atv/sample/holder/IconTreeItemHolder.java create mode 100644 app/src/main/java/com/unnamed/b/atv/sample/holder/IconTreeItemHolder.kt delete mode 100644 app/src/main/java/com/unnamed/b/atv/sample/holder/PlaceHolderHolder.java create mode 100644 app/src/main/java/com/unnamed/b/atv/sample/holder/PlaceHolderHolder.kt delete mode 100644 app/src/main/java/com/unnamed/b/atv/sample/holder/ProfileHolder.java create mode 100644 app/src/main/java/com/unnamed/b/atv/sample/holder/ProfileHolder.kt delete mode 100644 app/src/main/java/com/unnamed/b/atv/sample/holder/SelectableHeaderHolder.java create mode 100644 app/src/main/java/com/unnamed/b/atv/sample/holder/SelectableHeaderHolder.kt delete mode 100644 app/src/main/java/com/unnamed/b/atv/sample/holder/SelectableItemHolder.java create mode 100644 app/src/main/java/com/unnamed/b/atv/sample/holder/SelectableItemHolder.kt delete mode 100644 app/src/main/java/com/unnamed/b/atv/sample/holder/SocialViewHolder.java create mode 100644 app/src/main/java/com/unnamed/b/atv/sample/holder/SocialViewHolder.kt delete mode 100644 library/src/main/java/com/unnamed/b/atv/holder/SimpleViewHolder.java create mode 100644 library/src/main/java/com/unnamed/b/atv/holder/SimpleViewHolder.kt delete mode 100644 library/src/main/java/com/unnamed/b/atv/model/TreeNode.java create mode 100644 library/src/main/java/com/unnamed/b/atv/model/TreeNode.kt delete mode 100644 library/src/main/java/com/unnamed/b/atv/view/AndroidTreeView.java create mode 100644 library/src/main/java/com/unnamed/b/atv/view/AndroidTreeView.kt delete mode 100644 library/src/main/java/com/unnamed/b/atv/view/TreeNodeWrapperView.java create mode 100644 library/src/main/java/com/unnamed/b/atv/view/TreeNodeWrapperView.kt delete mode 100644 library/src/main/java/com/unnamed/b/atv/view/TwoDScrollView.java create mode 100644 library/src/main/java/com/unnamed/b/atv/view/TwoDScrollView.kt diff --git a/.gitignore b/.gitignore index e08d3d5..d90d92b 100644 --- a/.gitignore +++ b/.gitignore @@ -23,4 +23,6 @@ local.properties # Gradle .gradle -build/ \ No newline at end of file +build/ +.idea/instapk.xml +instapk.log* \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 6d144bb..828f781 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,4 +1,5 @@ apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' android { compileSdkVersion Integer.parseInt(project.ANDROID_BUILD_SDK_VERSION) @@ -20,7 +21,8 @@ android { dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) - compile 'com.android.support:appcompat-v7:21.0.3' - compile 'com.github.johnkil.print:print:1.2.2' + compile 'com.android.support:appcompat-v7:25.3.1' + compile 'com.github.johnkil.print:print:1.3.1' + compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version" compile project(':library') } diff --git a/app/src/main/java/com/unnamed/b/atv/sample/activity/MainActivity.java b/app/src/main/java/com/unnamed/b/atv/sample/activity/MainActivity.java deleted file mode 100644 index f74bea2..0000000 --- a/app/src/main/java/com/unnamed/b/atv/sample/activity/MainActivity.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.unnamed.b.atv.sample.activity; - -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; -import android.support.v7.app.ActionBarActivity; -import android.view.View; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.ListView; - -import com.unnamed.b.atv.sample.R; -import com.unnamed.b.atv.sample.fragment.CustomViewHolderFragment; -import com.unnamed.b.atv.sample.fragment.FolderStructureFragment; -import com.unnamed.b.atv.sample.fragment.SelectableTreeFragment; -import com.unnamed.b.atv.sample.fragment.TwoDScrollingArrowExpandFragment; -import com.unnamed.b.atv.sample.fragment.TwoDScrollingFragment; - -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; - - -public class MainActivity extends ActionBarActivity { - - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); - - - final LinkedHashMap> listItems = new LinkedHashMap<>(); - listItems.put("Folder Structure Example", FolderStructureFragment.class); - listItems.put("Custom Holder Example", CustomViewHolderFragment.class); - listItems.put("Selectable Nodes", SelectableTreeFragment.class); - listItems.put("2d scrolling", TwoDScrollingFragment.class); - listItems.put("Expand with arrow only", TwoDScrollingArrowExpandFragment.class); - - - final List list = new ArrayList(listItems.keySet()); - final ListView listview = (ListView) findViewById(R.id.listview); - final SimpleArrayAdapter adapter = new SimpleArrayAdapter(this, list); - listview.setAdapter(adapter); - listview.setOnItemClickListener(new AdapterView.OnItemClickListener() { - @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - Class clazz = listItems.values().toArray(new Class[]{})[position]; - Intent i = new Intent(MainActivity.this, SingleFragmentActivity.class); - i.putExtra(SingleFragmentActivity.FRAGMENT_PARAM, clazz); - MainActivity.this.startActivity(i); - } - }); - - } - - private class SimpleArrayAdapter extends ArrayAdapter { - public SimpleArrayAdapter(Context context, List objects) { - super(context, android.R.layout.simple_list_item_1, objects); - - } - - @Override - public long getItemId(int position) { - return position; - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/unnamed/b/atv/sample/activity/MainActivity.kt b/app/src/main/java/com/unnamed/b/atv/sample/activity/MainActivity.kt new file mode 100644 index 0000000..6f85c25 --- /dev/null +++ b/app/src/main/java/com/unnamed/b/atv/sample/activity/MainActivity.kt @@ -0,0 +1,52 @@ +package com.unnamed.b.atv.sample.activity + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.support.v7.app.AppCompatActivity +import android.widget.AdapterView +import android.widget.ArrayAdapter +import android.widget.ListView +import com.unnamed.b.atv.sample.R +import com.unnamed.b.atv.sample.fragment.* + +/** +* Converted to Kolin by Kumar Shivang on 16/07/17 +*/ + +class MainActivity : AppCompatActivity() { + + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) + + + val listItems = LinkedHashMap>() + listItems.put("Folder Structure Example", FolderStructureFragment::class.java) + listItems.put("Custom Holder Example", CustomViewHolderFragment::class.java) + listItems.put("Selectable Nodes", SelectableTreeFragment::class.java) + listItems.put("2d scrolling", TwoDScrollingFragment::class.java) + listItems.put("Expand with arrow only", TwoDScrollingArrowExpandFragment::class.java) + + + val list = ArrayList(listItems.keys) + val listview = findViewById(R.id.listview) as ListView + val adapter = SimpleArrayAdapter(this, list) + listview.adapter = adapter + listview.onItemClickListener = AdapterView.OnItemClickListener { parent, view, position, id -> + val clazz = listItems.values.toTypedArray()[position] + val i = Intent(this@MainActivity, SingleFragmentActivity::class.java) + i.putExtra(SingleFragmentActivity.FRAGMENT_PARAM, clazz) + this@MainActivity.startActivity(i) + } + + } + + private inner class SimpleArrayAdapter(context: Context, objects: List) : ArrayAdapter(context, android.R.layout.simple_list_item_1, objects) { + + override fun getItemId(position: Int): Long { + return position.toLong() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/unnamed/b/atv/sample/activity/SingleFragmentActivity.java b/app/src/main/java/com/unnamed/b/atv/sample/activity/SingleFragmentActivity.java deleted file mode 100644 index c28f65b..0000000 --- a/app/src/main/java/com/unnamed/b/atv/sample/activity/SingleFragmentActivity.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.unnamed.b.atv.sample.activity; - -import android.app.Fragment; -import android.os.Bundle; -import android.support.v7.app.ActionBarActivity; - -import com.unnamed.b.atv.sample.R; - -/** - * Created by Bogdan Melnychuk on 2/12/15. - */ -public class SingleFragmentActivity extends ActionBarActivity { - public final static String FRAGMENT_PARAM = "fragment"; - - @Override - protected void onCreate(Bundle bundle) { - super.onCreate(bundle); - setContentView(R.layout.activity_single_fragment); - - Bundle b = getIntent().getExtras(); - Class fragmentClass = (Class) b.get(FRAGMENT_PARAM); - if (bundle == null) { - Fragment f = Fragment.instantiate(this, fragmentClass.getName()); - f.setArguments(b); - getFragmentManager().beginTransaction().replace(R.id.fragment, f, fragmentClass.getName()).commit(); - } - } -} diff --git a/app/src/main/java/com/unnamed/b/atv/sample/activity/SingleFragmentActivity.kt b/app/src/main/java/com/unnamed/b/atv/sample/activity/SingleFragmentActivity.kt new file mode 100644 index 0000000..abfd4bd --- /dev/null +++ b/app/src/main/java/com/unnamed/b/atv/sample/activity/SingleFragmentActivity.kt @@ -0,0 +1,30 @@ +package com.unnamed.b.atv.sample.activity + +import android.os.Bundle +import android.support.v4.app.Fragment +import android.support.v7.app.AppCompatActivity +import com.unnamed.b.atv.sample.R + +/** + * Created by Bogdan Melnychuk on 2/12/15. + * Converted to Kolin by Kumar Shivang on 16/07/17 + */ +class SingleFragmentActivity : AppCompatActivity() { + + override fun onCreate(bundle: Bundle?) { + super.onCreate(bundle) + setContentView(R.layout.activity_single_fragment) + + val b = intent.extras + val fragmentClass = b.get(FRAGMENT_PARAM) as Class<*> + if (bundle == null) { + val f = Fragment.instantiate(this, fragmentClass.name) + f.arguments = b + supportFragmentManager.beginTransaction().replace(R.id.fragment, f, fragmentClass.name).commit() + } + } + + companion object { + val FRAGMENT_PARAM = "fragment" + } +} diff --git a/app/src/main/java/com/unnamed/b/atv/sample/fragment/CustomViewHolderFragment.java b/app/src/main/java/com/unnamed/b/atv/sample/fragment/CustomViewHolderFragment.java deleted file mode 100644 index 77c96f4..0000000 --- a/app/src/main/java/com/unnamed/b/atv/sample/fragment/CustomViewHolderFragment.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.unnamed.b.atv.sample.fragment; - -import android.app.Fragment; -import android.os.Bundle; -import android.text.TextUtils; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import com.unnamed.b.atv.model.TreeNode; -import com.unnamed.b.atv.sample.R; -import com.unnamed.b.atv.sample.holder.HeaderHolder; -import com.unnamed.b.atv.sample.holder.IconTreeItemHolder; -import com.unnamed.b.atv.sample.holder.PlaceHolderHolder; -import com.unnamed.b.atv.sample.holder.ProfileHolder; -import com.unnamed.b.atv.sample.holder.SocialViewHolder; -import com.unnamed.b.atv.view.AndroidTreeView; - -/** - * Created by Bogdan Melnychuk on 2/12/15. - */ -public class CustomViewHolderFragment extends Fragment { - private AndroidTreeView tView; - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - final View rootView = inflater.inflate(R.layout.fragment_default, null, false); - final ViewGroup containerView = (ViewGroup) rootView.findViewById(R.id.container); - rootView.findViewById(R.id.status_bar).setVisibility(View.GONE); - - final TreeNode root = TreeNode.root(); - - TreeNode myProfile = new TreeNode(new IconTreeItemHolder.IconTreeItem(R.string.ic_person, "My Profile")).setViewHolder(new ProfileHolder(getActivity())); - TreeNode bruce = new TreeNode(new IconTreeItemHolder.IconTreeItem(R.string.ic_person, "Bruce Wayne")).setViewHolder(new ProfileHolder(getActivity())); - TreeNode clark = new TreeNode(new IconTreeItemHolder.IconTreeItem(R.string.ic_person, "Clark Kent")).setViewHolder(new ProfileHolder(getActivity())); - TreeNode barry = new TreeNode(new IconTreeItemHolder.IconTreeItem(R.string.ic_person, "Barry Allen")).setViewHolder(new ProfileHolder(getActivity())); - addProfileData(myProfile); - addProfileData(clark); - addProfileData(bruce); - addProfileData(barry); - root.addChildren(myProfile, bruce, barry, clark); - - tView = new AndroidTreeView(getActivity(), root); - tView.setDefaultAnimation(true); - tView.setDefaultContainerStyle(R.style.TreeNodeStyleDivided, true); - containerView.addView(tView.getView()); - - if (savedInstanceState != null) { - String state = savedInstanceState.getString("tState"); - if (!TextUtils.isEmpty(state)) { - tView.restoreState(state); - } - } - - return rootView; - } - - private void addProfileData(TreeNode profile) { - TreeNode socialNetworks = new TreeNode(new IconTreeItemHolder.IconTreeItem(R.string.ic_people, "Social")).setViewHolder(new HeaderHolder(getActivity())); - TreeNode places = new TreeNode(new IconTreeItemHolder.IconTreeItem(R.string.ic_place, "Places")).setViewHolder(new HeaderHolder(getActivity())); - - TreeNode facebook = new TreeNode(new SocialViewHolder.SocialItem(R.string.ic_post_facebook)).setViewHolder(new SocialViewHolder(getActivity())); - TreeNode linkedin = new TreeNode(new SocialViewHolder.SocialItem(R.string.ic_post_linkedin)).setViewHolder(new SocialViewHolder(getActivity())); - TreeNode google = new TreeNode(new SocialViewHolder.SocialItem(R.string.ic_post_gplus)).setViewHolder(new SocialViewHolder(getActivity())); - TreeNode twitter = new TreeNode(new SocialViewHolder.SocialItem(R.string.ic_post_twitter)).setViewHolder(new SocialViewHolder(getActivity())); - - TreeNode lake = new TreeNode(new PlaceHolderHolder.PlaceItem("A rose garden")).setViewHolder(new PlaceHolderHolder(getActivity())); - TreeNode mountains = new TreeNode(new PlaceHolderHolder.PlaceItem("The white house")).setViewHolder(new PlaceHolderHolder(getActivity())); - - places.addChildren(lake, mountains); - socialNetworks.addChildren(facebook, google, twitter, linkedin); - profile.addChildren(socialNetworks, places); - } - - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - outState.putString("tState", tView.getSaveState()); - } -} diff --git a/app/src/main/java/com/unnamed/b/atv/sample/fragment/CustomViewHolderFragment.kt b/app/src/main/java/com/unnamed/b/atv/sample/fragment/CustomViewHolderFragment.kt new file mode 100644 index 0000000..188e86b --- /dev/null +++ b/app/src/main/java/com/unnamed/b/atv/sample/fragment/CustomViewHolderFragment.kt @@ -0,0 +1,75 @@ +package com.unnamed.b.atv.sample.fragment + +import android.os.Bundle +import android.support.v4.app.Fragment +import android.text.TextUtils +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.unnamed.b.atv.model.TreeNode +import com.unnamed.b.atv.sample.R +import com.unnamed.b.atv.sample.holder.* +import com.unnamed.b.atv.view.AndroidTreeView + + +/** + * Created by Bogdan Melnychuk on 2/12/15. + * Converted to Kolin by Kumar Shivang on 16/07/17 + */ +class CustomViewHolderFragment : Fragment() { + private var tView: AndroidTreeView? = null + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + val rootView = inflater.inflate(R.layout.fragment_default, container, false) + val containerView = rootView.findViewById(R.id.container) as ViewGroup + rootView.findViewById(R.id.status_bar).visibility = View.GONE + + val root = TreeNode.root() + + val myProfile = TreeNode(IconTreeItemHolder.IconTreeItem(R.string.ic_person, "My Profile")).setViewHolder(ProfileHolder(activity)) + val bruce = TreeNode(IconTreeItemHolder.IconTreeItem(R.string.ic_person, "Bruce Wayne")).setViewHolder(ProfileHolder(activity)) + val clark = TreeNode(IconTreeItemHolder.IconTreeItem(R.string.ic_person, "Clark Kent")).setViewHolder(ProfileHolder(activity)) + val barry = TreeNode(IconTreeItemHolder.IconTreeItem(R.string.ic_person, "Barry Allen")).setViewHolder(ProfileHolder(activity)) + addProfileData(myProfile) + addProfileData(clark) + addProfileData(bruce) + addProfileData(barry) + root.addChildren(myProfile, bruce, barry, clark) + + tView = AndroidTreeView(activity, root) + tView!!.setDefaultAnimation(true) + tView!!.setDefaultContainerStyle(R.style.TreeNodeStyleDivided, true) + containerView.addView(tView!!.view) + + if (savedInstanceState != null) { + val state = savedInstanceState.getString("tState") + if (!TextUtils.isEmpty(state)) { + tView!!.restoreState(state!!) + } + } + + return rootView + } + + private fun addProfileData(profile: TreeNode) { + val socialNetworks = TreeNode(IconTreeItemHolder.IconTreeItem(R.string.ic_people, "Social")).setViewHolder(HeaderHolder(activity)) + val places = TreeNode(IconTreeItemHolder.IconTreeItem(R.string.ic_place, "Places")).setViewHolder(HeaderHolder(activity)) + + val facebook = TreeNode(SocialViewHolder.SocialItem(R.string.ic_post_facebook)).setViewHolder(SocialViewHolder(activity)) + val linkedin = TreeNode(SocialViewHolder.SocialItem(R.string.ic_post_linkedin)).setViewHolder(SocialViewHolder(activity)) + val google = TreeNode(SocialViewHolder.SocialItem(R.string.ic_post_gplus)).setViewHolder(SocialViewHolder(activity)) + val twitter = TreeNode(SocialViewHolder.SocialItem(R.string.ic_post_twitter)).setViewHolder(SocialViewHolder(activity)) + + val lake = TreeNode(PlaceHolderHolder.PlaceItem("A rose garden")).setViewHolder(PlaceHolderHolder(activity)) + val mountains = TreeNode(PlaceHolderHolder.PlaceItem("The white house")).setViewHolder(PlaceHolderHolder(activity)) + + places.addChildren(lake, mountains) + socialNetworks.addChildren(facebook, google, twitter, linkedin) + profile.addChildren(socialNetworks, places) + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putString("tState", tView!!.saveState) + } +} diff --git a/app/src/main/java/com/unnamed/b/atv/sample/fragment/FolderStructureFragment.java b/app/src/main/java/com/unnamed/b/atv/sample/fragment/FolderStructureFragment.java deleted file mode 100644 index a59790c..0000000 --- a/app/src/main/java/com/unnamed/b/atv/sample/fragment/FolderStructureFragment.java +++ /dev/null @@ -1,136 +0,0 @@ -package com.unnamed.b.atv.sample.fragment; - -import android.app.Fragment; -import android.os.Bundle; -import android.text.TextUtils; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; -import android.widget.Toast; - -import com.unnamed.b.atv.model.TreeNode; -import com.unnamed.b.atv.sample.R; -import com.unnamed.b.atv.sample.holder.IconTreeItemHolder; -import com.unnamed.b.atv.view.AndroidTreeView; - -/** - * Created by Bogdan Melnychuk on 2/12/15. - */ -public class FolderStructureFragment extends Fragment { - private TextView statusBar; - private AndroidTreeView tView; - - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setHasOptionsMenu(true); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - - View rootView = inflater.inflate(R.layout.fragment_default, null, false); - ViewGroup containerView = (ViewGroup) rootView.findViewById(R.id.container); - - statusBar = (TextView) rootView.findViewById(R.id.status_bar); - - TreeNode root = TreeNode.root(); - TreeNode computerRoot = new TreeNode(new IconTreeItemHolder.IconTreeItem(R.string.ic_laptop, "My Computer")); - - TreeNode myDocuments = new TreeNode(new IconTreeItemHolder.IconTreeItem(R.string.ic_folder, "My Documents")); - TreeNode downloads = new TreeNode(new IconTreeItemHolder.IconTreeItem(R.string.ic_folder, "Downloads")); - TreeNode file1 = new TreeNode(new IconTreeItemHolder.IconTreeItem(R.string.ic_drive_file, "Folder 1")); - TreeNode file2 = new TreeNode(new IconTreeItemHolder.IconTreeItem(R.string.ic_drive_file, "Folder 2")); - TreeNode file3 = new TreeNode(new IconTreeItemHolder.IconTreeItem(R.string.ic_drive_file, "Folder 3")); - TreeNode file4 = new TreeNode(new IconTreeItemHolder.IconTreeItem(R.string.ic_drive_file, "Folder 4")); - fillDownloadsFolder(downloads); - downloads.addChildren(file1, file2, file3, file4); - - TreeNode myMedia = new TreeNode(new IconTreeItemHolder.IconTreeItem(R.string.ic_photo_library, "Photos")); - TreeNode photo1 = new TreeNode(new IconTreeItemHolder.IconTreeItem(R.string.ic_photo, "Folder 1")); - TreeNode photo2 = new TreeNode(new IconTreeItemHolder.IconTreeItem(R.string.ic_photo, "Folder 2")); - TreeNode photo3 = new TreeNode(new IconTreeItemHolder.IconTreeItem(R.string.ic_photo, "Folder 3")); - myMedia.addChildren(photo1, photo2, photo3); - - myDocuments.addChild(downloads); - computerRoot.addChildren(myDocuments, myMedia); - - root.addChildren(computerRoot); - - tView = new AndroidTreeView(getActivity(), root); - tView.setDefaultAnimation(true); - tView.setDefaultContainerStyle(R.style.TreeNodeStyleCustom); - tView.setDefaultViewHolder(IconTreeItemHolder.class); - tView.setDefaultNodeClickListener(nodeClickListener); - tView.setDefaultNodeLongClickListener(nodeLongClickListener); - - containerView.addView(tView.getView()); - - if (savedInstanceState != null) { - String state = savedInstanceState.getString("tState"); - if (!TextUtils.isEmpty(state)) { - tView.restoreState(state); - } - } - - return rootView; - } - - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - inflater.inflate(R.menu.menu, menu); - super.onCreateOptionsMenu(menu, inflater); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.expandAll: - tView.expandAll(); - break; - - case R.id.collapseAll: - tView.collapseAll(); - break; - } - return true; - } - - private int counter = 0; - - private void fillDownloadsFolder(TreeNode node) { - TreeNode downloads = new TreeNode(new IconTreeItemHolder.IconTreeItem(R.string.ic_folder, "Downloads" + (counter++))); - node.addChild(downloads); - if (counter < 5) { - fillDownloadsFolder(downloads); - } - } - - private TreeNode.TreeNodeClickListener nodeClickListener = new TreeNode.TreeNodeClickListener() { - @Override - public void onClick(TreeNode node, Object value) { - IconTreeItemHolder.IconTreeItem item = (IconTreeItemHolder.IconTreeItem) value; - statusBar.setText("Last clicked: " + item.text); - } - }; - - private TreeNode.TreeNodeLongClickListener nodeLongClickListener = new TreeNode.TreeNodeLongClickListener() { - @Override - public boolean onLongClick(TreeNode node, Object value) { - IconTreeItemHolder.IconTreeItem item = (IconTreeItemHolder.IconTreeItem) value; - Toast.makeText(getActivity(), "Long click: " + item.text, Toast.LENGTH_SHORT).show(); - return true; - } - }; - - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - outState.putString("tState", tView.getSaveState()); - } -} diff --git a/app/src/main/java/com/unnamed/b/atv/sample/fragment/FolderStructureFragment.kt b/app/src/main/java/com/unnamed/b/atv/sample/fragment/FolderStructureFragment.kt new file mode 100644 index 0000000..e2948cd --- /dev/null +++ b/app/src/main/java/com/unnamed/b/atv/sample/fragment/FolderStructureFragment.kt @@ -0,0 +1,121 @@ +package com.unnamed.b.atv.sample.fragment + +import android.os.Bundle +import android.support.v4.app.Fragment +import android.text.TextUtils +import android.view.* +import android.widget.TextView +import android.widget.Toast +import com.unnamed.b.atv.model.TreeNode +import com.unnamed.b.atv.sample.R +import com.unnamed.b.atv.sample.holder.IconTreeItemHolder +import com.unnamed.b.atv.view.AndroidTreeView + + +/** + * Created by Bogdan Melnychuk on 2/12/15. + * Converted to Kolin by Kumar Shivang on 16/07/17 + */ +class FolderStructureFragment : Fragment() { + private var statusBar: TextView? = null + private var tView: AndroidTreeView? = null + + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setHasOptionsMenu(true) + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + + val rootView = inflater.inflate(R.layout.fragment_default, container, false) + val containerView = rootView.findViewById(R.id.container) as ViewGroup + + statusBar = rootView.findViewById(R.id.status_bar) as TextView + + val root = TreeNode.root() + val computerRoot = TreeNode(IconTreeItemHolder.IconTreeItem(R.string.ic_laptop, "My Computer")) + + val myDocuments = TreeNode(IconTreeItemHolder.IconTreeItem(R.string.ic_folder, "My Documents")) + val downloads = TreeNode(IconTreeItemHolder.IconTreeItem(R.string.ic_folder, "Downloads")) + val file1 = TreeNode(IconTreeItemHolder.IconTreeItem(R.string.ic_drive_file, "Folder 1")) + val file2 = TreeNode(IconTreeItemHolder.IconTreeItem(R.string.ic_drive_file, "Folder 2")) + val file3 = TreeNode(IconTreeItemHolder.IconTreeItem(R.string.ic_drive_file, "Folder 3")) + val file4 = TreeNode(IconTreeItemHolder.IconTreeItem(R.string.ic_drive_file, "Folder 4")) + fillDownloadsFolder(downloads) + downloads.addChildren(file1, file2, file3, file4) + + val myMedia = TreeNode(IconTreeItemHolder.IconTreeItem(R.string.ic_photo_library, "Photos")) + val photo1 = TreeNode(IconTreeItemHolder.IconTreeItem(R.string.ic_photo, "Folder 1")) + val photo2 = TreeNode(IconTreeItemHolder.IconTreeItem(R.string.ic_photo, "Folder 2")) + val photo3 = TreeNode(IconTreeItemHolder.IconTreeItem(R.string.ic_photo, "Folder 3")) + myMedia.addChildren(photo1, photo2, photo3) + + myDocuments.addChild(downloads) + computerRoot.addChildren(myDocuments, myMedia) + + root.addChildren(computerRoot) + + tView = AndroidTreeView(activity, root) + tView!!.setDefaultAnimation(true) + tView!!.setDefaultContainerStyle(R.style.TreeNodeStyleCustom) + tView!!.setDefaultViewHolder(IconTreeItemHolder::class.java) + tView!!.setDefaultNodeClickListener(nodeClickListener) + tView!!.setDefaultNodeLongClickListener(nodeLongClickListener) + + containerView.addView(tView!!.view) + + if (savedInstanceState != null) { + val state = savedInstanceState.getString("tState") + if (!TextUtils.isEmpty(state)) { + tView!!.restoreState(state!!) + } + } + + return rootView + } + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + inflater.inflate(R.menu.menu, menu) + super.onCreateOptionsMenu(menu, inflater) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + R.id.expandAll -> tView!!.expandAll() + + R.id.collapseAll -> tView!!.collapseAll() + } + return true + } + + private var counter = 0 + + private fun fillDownloadsFolder(node: TreeNode) { + val downloads = TreeNode(IconTreeItemHolder.IconTreeItem(R.string.ic_folder, "Downloads" + counter++)) + node.addChild(downloads) + if (counter < 5) { + fillDownloadsFolder(downloads) + } + } + + private val nodeClickListener = object : TreeNode.TreeNodeClickListener { + override fun onClick(node: TreeNode, value: Any) { + val item = value as IconTreeItemHolder.IconTreeItem + statusBar!!.text = "Last clicked: " + item.text + } + } + + private val nodeLongClickListener = object : TreeNode.TreeNodeLongClickListener { + override fun onLongClick(node: TreeNode, value: Any): Boolean { + val item = value as IconTreeItemHolder.IconTreeItem + Toast.makeText(activity, "Long click: " + item.text, Toast.LENGTH_SHORT).show() + return true + } + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putString("tState", tView!!.saveState) + } +} diff --git a/app/src/main/java/com/unnamed/b/atv/sample/fragment/SelectableTreeFragment.java b/app/src/main/java/com/unnamed/b/atv/sample/fragment/SelectableTreeFragment.java deleted file mode 100644 index 3a1a151..0000000 --- a/app/src/main/java/com/unnamed/b/atv/sample/fragment/SelectableTreeFragment.java +++ /dev/null @@ -1,120 +0,0 @@ -package com.unnamed.b.atv.sample.fragment; - -import android.app.Fragment; -import android.os.Bundle; -import android.text.TextUtils; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Toast; - -import com.unnamed.b.atv.model.TreeNode; -import com.unnamed.b.atv.sample.R; -import com.unnamed.b.atv.sample.holder.IconTreeItemHolder; -import com.unnamed.b.atv.sample.holder.ProfileHolder; -import com.unnamed.b.atv.sample.holder.SelectableHeaderHolder; -import com.unnamed.b.atv.sample.holder.SelectableItemHolder; -import com.unnamed.b.atv.view.AndroidTreeView; - -/** - * Created by Bogdan Melnychuk on 2/12/15. - */ -public class SelectableTreeFragment extends Fragment { - private AndroidTreeView tView; - private boolean selectionModeEnabled = false; - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - - View rootView = inflater.inflate(R.layout.fragment_selectable_nodes, null, false); - ViewGroup containerView = (ViewGroup) rootView.findViewById(R.id.container); - - View selectionModeButton = rootView.findViewById(R.id.btn_toggleSelection); - selectionModeButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - selectionModeEnabled = !selectionModeEnabled; - tView.setSelectionModeEnabled(selectionModeEnabled); - } - }); - - View selectAllBtn = rootView.findViewById(R.id.btn_selectAll); - selectAllBtn.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (!selectionModeEnabled) { - Toast.makeText(getActivity(), "Enable selection mode first", Toast.LENGTH_SHORT).show(); - } - tView.selectAll(true); - } - }); - - View deselectAll = rootView.findViewById(R.id.btn_deselectAll); - deselectAll.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (!selectionModeEnabled) { - Toast.makeText(getActivity(), "Enable selection mode first", Toast.LENGTH_SHORT).show(); - } - tView.deselectAll(); - } - }); - - View check = rootView.findViewById(R.id.btn_checkSelection); - check.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (!selectionModeEnabled) { - Toast.makeText(getActivity(), "Enable selection mode first", Toast.LENGTH_SHORT).show(); - } else { - Toast.makeText(getActivity(), tView.getSelected().size() + " selected", Toast.LENGTH_SHORT).show(); - } - } - }); - - TreeNode root = TreeNode.root(); - - TreeNode s1 = new TreeNode(new IconTreeItemHolder.IconTreeItem(R.string.ic_sd_storage, "Storage1")).setViewHolder(new ProfileHolder(getActivity())); - TreeNode s2 = new TreeNode(new IconTreeItemHolder.IconTreeItem(R.string.ic_sd_storage, "Storage2")).setViewHolder(new ProfileHolder(getActivity())); - s1.setSelectable(false); - s2.setSelectable(false); - - TreeNode folder1 = new TreeNode(new IconTreeItemHolder.IconTreeItem(R.string.ic_folder, "Folder 1")).setViewHolder(new SelectableHeaderHolder(getActivity())); - TreeNode folder2 = new TreeNode(new IconTreeItemHolder.IconTreeItem(R.string.ic_folder, "Folder 2")).setViewHolder(new SelectableHeaderHolder(getActivity())); - TreeNode folder3 = new TreeNode(new IconTreeItemHolder.IconTreeItem(R.string.ic_folder, "Folder 3")).setViewHolder(new SelectableHeaderHolder(getActivity())); - - fillFolder(folder1); - fillFolder(folder2); - fillFolder(folder3); - - s1.addChildren(folder1, folder2); - s2.addChildren(folder3); - - root.addChildren(s1, s2); - - tView = new AndroidTreeView(getActivity(), root); - tView.setDefaultAnimation(true); - containerView.addView(tView.getView()); - - if (savedInstanceState != null) { - String state = savedInstanceState.getString("tState"); - if (!TextUtils.isEmpty(state)) { - tView.restoreState(state); - } - } - return rootView; - } - - private void fillFolder(TreeNode folder) { - TreeNode file1 = new TreeNode("File1").setViewHolder(new SelectableItemHolder(getActivity())); - TreeNode file2 = new TreeNode("File2").setViewHolder(new SelectableItemHolder(getActivity())); - TreeNode file3 = new TreeNode("File3").setViewHolder(new SelectableItemHolder(getActivity())); - folder.addChildren(file1, file2, file3); - } - - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - outState.putString("tState", tView.getSaveState()); - } -} diff --git a/app/src/main/java/com/unnamed/b/atv/sample/fragment/SelectableTreeFragment.kt b/app/src/main/java/com/unnamed/b/atv/sample/fragment/SelectableTreeFragment.kt new file mode 100644 index 0000000..bfff4f2 --- /dev/null +++ b/app/src/main/java/com/unnamed/b/atv/sample/fragment/SelectableTreeFragment.kt @@ -0,0 +1,107 @@ +package com.unnamed.b.atv.sample.fragment + +import android.os.Bundle +import android.support.v4.app.Fragment +import android.text.TextUtils +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import com.unnamed.b.atv.model.TreeNode +import com.unnamed.b.atv.sample.R +import com.unnamed.b.atv.sample.holder.IconTreeItemHolder +import com.unnamed.b.atv.sample.holder.ProfileHolder +import com.unnamed.b.atv.sample.holder.SelectableHeaderHolder +import com.unnamed.b.atv.sample.holder.SelectableItemHolder +import com.unnamed.b.atv.view.AndroidTreeView + + +/** + * Created by Bogdan Melnychuk on 2/12/15. + * Converted to Kolin by Kumar Shivang on 16/07/17 + */ +class SelectableTreeFragment : Fragment() { + private var tView: AndroidTreeView? = null + private var selectionModeEnabled = false + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + + val rootView = inflater.inflate(R.layout.fragment_selectable_nodes, null, false) + val containerView = rootView.findViewById(R.id.container) as ViewGroup + + val selectionModeButton = rootView.findViewById(R.id.btn_toggleSelection) + selectionModeButton.setOnClickListener { + selectionModeEnabled = !selectionModeEnabled + tView!!.isSelectionModeEnabled = selectionModeEnabled + } + + val selectAllBtn = rootView.findViewById(R.id.btn_selectAll) + selectAllBtn.setOnClickListener { + if (!selectionModeEnabled) { + Toast.makeText(activity, "Enable selection mode first", Toast.LENGTH_SHORT).show() + } + tView!!.selectAll(true) + } + + val deselectAll = rootView.findViewById(R.id.btn_deselectAll) + deselectAll.setOnClickListener { + if (!selectionModeEnabled) { + Toast.makeText(activity, "Enable selection mode first", Toast.LENGTH_SHORT).show() + } + tView!!.deselectAll() + } + + val check = rootView.findViewById(R.id.btn_checkSelection) + check.setOnClickListener { + if (!selectionModeEnabled) { + Toast.makeText(activity, "Enable selection mode first", Toast.LENGTH_SHORT).show() + } else { + Toast.makeText(activity, tView!!.selected.size.toString() + " selected", Toast.LENGTH_SHORT).show() + } + } + + val root = TreeNode.root() + + val s1 = TreeNode(IconTreeItemHolder.IconTreeItem(R.string.ic_sd_storage, "Storage1")).setViewHolder(ProfileHolder(activity)) + val s2 = TreeNode(IconTreeItemHolder.IconTreeItem(R.string.ic_sd_storage, "Storage2")).setViewHolder(ProfileHolder(activity)) + s1.isSelectable = false + s2.isSelectable = false + + val folder1 = TreeNode(IconTreeItemHolder.IconTreeItem(R.string.ic_folder, "Folder 1")).setViewHolder(SelectableHeaderHolder(activity)) + val folder2 = TreeNode(IconTreeItemHolder.IconTreeItem(R.string.ic_folder, "Folder 2")).setViewHolder(SelectableHeaderHolder(activity)) + val folder3 = TreeNode(IconTreeItemHolder.IconTreeItem(R.string.ic_folder, "Folder 3")).setViewHolder(SelectableHeaderHolder(activity)) + + fillFolder(folder1) + fillFolder(folder2) + fillFolder(folder3) + + s1.addChildren(folder1, folder2) + s2.addChildren(folder3) + + root.addChildren(s1, s2) + + tView = AndroidTreeView(activity, root) + tView!!.setDefaultAnimation(true) + containerView.addView(tView!!.view) + + if (savedInstanceState != null) { + val state = savedInstanceState.getString("tState") + if (!TextUtils.isEmpty(state)) { + tView!!.restoreState(state!!) + } + } + return rootView + } + + private fun fillFolder(folder: TreeNode) { + val file1 = TreeNode("File1").setViewHolder(SelectableItemHolder(activity)) + val file2 = TreeNode("File2").setViewHolder(SelectableItemHolder(activity)) + val file3 = TreeNode("File3").setViewHolder(SelectableItemHolder(activity)) + folder.addChildren(file1, file2, file3) + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putString("tState", tView!!.saveState) + } +} diff --git a/app/src/main/java/com/unnamed/b/atv/sample/fragment/TwoDScrollingArrowExpandFragment.java b/app/src/main/java/com/unnamed/b/atv/sample/fragment/TwoDScrollingArrowExpandFragment.java deleted file mode 100644 index bc64194..0000000 --- a/app/src/main/java/com/unnamed/b/atv/sample/fragment/TwoDScrollingArrowExpandFragment.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.unnamed.b.atv.sample.fragment; - -import android.app.Fragment; -import android.os.Bundle; -import android.text.TextUtils; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Toast; - -import com.unnamed.b.atv.model.TreeNode; -import com.unnamed.b.atv.sample.R; -import com.unnamed.b.atv.sample.holder.ArrowExpandSelectableHeaderHolder; -import com.unnamed.b.atv.sample.holder.IconTreeItemHolder; -import com.unnamed.b.atv.view.AndroidTreeView; - -/** - * Created by Bogdan Melnychuk on 2/12/15 modified by Szigeti Peter 2/2/16. - */ -public class TwoDScrollingArrowExpandFragment extends Fragment implements TreeNode.TreeNodeClickListener{ - private static final String NAME = "Very long name for folder"; - private AndroidTreeView tView; - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View rootView = inflater.inflate(R.layout.fragment_selectable_nodes, null, false); - rootView.findViewById(R.id.status).setVisibility(View.GONE); - ViewGroup containerView = (ViewGroup) rootView.findViewById(R.id.container); - - TreeNode root = TreeNode.root(); - - TreeNode s1 = new TreeNode(new IconTreeItemHolder.IconTreeItem(R.string.ic_folder, "Folder with very long name ")).setViewHolder( - new ArrowExpandSelectableHeaderHolder(getActivity())); - TreeNode s2 = new TreeNode(new IconTreeItemHolder.IconTreeItem(R.string.ic_folder, "Another folder with very long name")).setViewHolder( - new ArrowExpandSelectableHeaderHolder(getActivity())); - - fillFolder(s1); - fillFolder(s2); - - root.addChildren(s1, s2); - - tView = new AndroidTreeView(getActivity(), root); - tView.setDefaultAnimation(true); - tView.setUse2dScroll(true); - tView.setDefaultContainerStyle(R.style.TreeNodeStyleCustom); - tView.setDefaultNodeClickListener(TwoDScrollingArrowExpandFragment.this); - tView.setDefaultViewHolder(ArrowExpandSelectableHeaderHolder.class); - containerView.addView(tView.getView()); - tView.setUseAutoToggle(false); - - tView.expandAll(); - - if (savedInstanceState != null) { - String state = savedInstanceState.getString("tState"); - if (!TextUtils.isEmpty(state)) { - tView.restoreState(state); - } - } - return rootView; - } - - private void fillFolder(TreeNode folder) { - TreeNode currentNode = folder; - for (int i = 0; i < 4; i++) { - TreeNode file = new TreeNode(new IconTreeItemHolder.IconTreeItem(R.string.ic_folder, NAME + " " + i)); - currentNode.addChild(file); - currentNode = file; - } - } - - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - outState.putString("tState", tView.getSaveState()); - } - - @Override - public void onClick(TreeNode node, Object value) { - Toast toast = Toast.makeText(getActivity(), ((IconTreeItemHolder.IconTreeItem)value).text, Toast.LENGTH_SHORT); - toast.show(); - } -} diff --git a/app/src/main/java/com/unnamed/b/atv/sample/fragment/TwoDScrollingArrowExpandFragment.kt b/app/src/main/java/com/unnamed/b/atv/sample/fragment/TwoDScrollingArrowExpandFragment.kt new file mode 100644 index 0000000..00760cb --- /dev/null +++ b/app/src/main/java/com/unnamed/b/atv/sample/fragment/TwoDScrollingArrowExpandFragment.kt @@ -0,0 +1,83 @@ +package com.unnamed.b.atv.sample.fragment + +import android.os.Bundle +import android.support.v4.app.Fragment +import android.text.TextUtils +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import com.unnamed.b.atv.model.TreeNode +import com.unnamed.b.atv.sample.R +import com.unnamed.b.atv.sample.holder.ArrowExpandSelectableHeaderHolder +import com.unnamed.b.atv.sample.holder.IconTreeItemHolder +import com.unnamed.b.atv.view.AndroidTreeView + + +/** + * Created by Bogdan Melnychuk on 2/12/15 modified by Szigeti Peter 2/2/16. + * Converted to Kolin by Kumar Shivang on 16/07/17 + */ +class TwoDScrollingArrowExpandFragment : Fragment(), TreeNode.TreeNodeClickListener { + private var tView: AndroidTreeView? = null + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + val rootView = inflater.inflate(R.layout.fragment_selectable_nodes, null, false) + rootView.findViewById(R.id.status).visibility = View.GONE + val containerView = rootView.findViewById(R.id.container) as ViewGroup + + val root = TreeNode.root() + + val s1 = TreeNode(IconTreeItemHolder.IconTreeItem(R.string.ic_folder, "Folder with very long name ")).setViewHolder( + ArrowExpandSelectableHeaderHolder(activity)) + val s2 = TreeNode(IconTreeItemHolder.IconTreeItem(R.string.ic_folder, "Another folder with very long name")).setViewHolder( + ArrowExpandSelectableHeaderHolder(activity)) + + fillFolder(s1) + fillFolder(s2) + + root.addChildren(s1, s2) + + tView = AndroidTreeView(activity, root) + tView!!.setDefaultAnimation(true) + tView!!.setUse2dScroll(true) + tView!!.setDefaultContainerStyle(R.style.TreeNodeStyleCustom) + tView!!.setDefaultNodeClickListener(this@TwoDScrollingArrowExpandFragment) + tView!!.setDefaultViewHolder(ArrowExpandSelectableHeaderHolder::class.java) + containerView.addView(tView!!.view) + tView!!.setUseAutoToggle(false) + + tView!!.expandAll() + + if (savedInstanceState != null) { + val state = savedInstanceState.getString("tState") + if (!TextUtils.isEmpty(state)) { + tView!!.restoreState(state!!) + } + } + return rootView + } + + private fun fillFolder(folder: TreeNode) { + var currentNode = folder + for (i in 0..3) { + val file = TreeNode(IconTreeItemHolder.IconTreeItem(R.string.ic_folder, NAME + " " + i)) + currentNode.addChild(file) + currentNode = file + } + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putString("tState", tView!!.saveState) + } + + override fun onClick(node: TreeNode, value: Any) { + val toast = Toast.makeText(activity, (value as IconTreeItemHolder.IconTreeItem).text, Toast.LENGTH_SHORT) + toast.show() + } + + companion object { + private val NAME = "Very long name for folder" + } +} diff --git a/app/src/main/java/com/unnamed/b/atv/sample/fragment/TwoDScrollingFragment.java b/app/src/main/java/com/unnamed/b/atv/sample/fragment/TwoDScrollingFragment.java deleted file mode 100644 index 37d62fe..0000000 --- a/app/src/main/java/com/unnamed/b/atv/sample/fragment/TwoDScrollingFragment.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.unnamed.b.atv.sample.fragment; - -import android.app.Fragment; -import android.os.Bundle; -import android.text.TextUtils; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import com.unnamed.b.atv.model.TreeNode; -import com.unnamed.b.atv.sample.R; -import com.unnamed.b.atv.sample.holder.IconTreeItemHolder; -import com.unnamed.b.atv.sample.holder.SelectableHeaderHolder; -import com.unnamed.b.atv.view.AndroidTreeView; - -/** - * Created by Bogdan Melnychuk on 2/12/15. - */ -public class TwoDScrollingFragment extends Fragment { - private static final String NAME = "Very long name for folder"; - private AndroidTreeView tView; - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View rootView = inflater.inflate(R.layout.fragment_selectable_nodes, null, false); - rootView.findViewById(R.id.status).setVisibility(View.GONE); - ViewGroup containerView = (ViewGroup) rootView.findViewById(R.id.container); - - TreeNode root = TreeNode.root(); - - TreeNode s1 = new TreeNode(new IconTreeItemHolder.IconTreeItem(R.string.ic_folder, "Folder with very long name ")).setViewHolder(new SelectableHeaderHolder(getActivity())); - TreeNode s2 = new TreeNode(new IconTreeItemHolder.IconTreeItem(R.string.ic_folder, "Another folder with very long name")).setViewHolder(new SelectableHeaderHolder(getActivity())); - - fillFolder(s1); - fillFolder(s2); - - root.addChildren(s1, s2); - - tView = new AndroidTreeView(getActivity(), root); - tView.setDefaultAnimation(true); - tView.setUse2dScroll(true); - tView.setDefaultContainerStyle(R.style.TreeNodeStyleCustom); - containerView.addView(tView.getView()); - - tView.expandAll(); - - if (savedInstanceState != null) { - String state = savedInstanceState.getString("tState"); - if (!TextUtils.isEmpty(state)) { - tView.restoreState(state); - } - } - return rootView; - } - - private void fillFolder(TreeNode folder) { - TreeNode currentNode = folder; - for (int i = 0; i < 10; i++) { - TreeNode file = new TreeNode(new IconTreeItemHolder.IconTreeItem(R.string.ic_folder, NAME)).setViewHolder(new SelectableHeaderHolder(getActivity())); - currentNode.addChild(file); - currentNode = file; - } - } - - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - outState.putString("tState", tView.getSaveState()); - } -} diff --git a/app/src/main/java/com/unnamed/b/atv/sample/fragment/TwoDScrollingFragment.kt b/app/src/main/java/com/unnamed/b/atv/sample/fragment/TwoDScrollingFragment.kt new file mode 100644 index 0000000..eedd4c3 --- /dev/null +++ b/app/src/main/java/com/unnamed/b/atv/sample/fragment/TwoDScrollingFragment.kt @@ -0,0 +1,72 @@ +package com.unnamed.b.atv.sample.fragment + +import android.os.Bundle +import android.support.v4.app.Fragment +import android.text.TextUtils +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup + +import com.unnamed.b.atv.model.TreeNode +import com.unnamed.b.atv.sample.R +import com.unnamed.b.atv.sample.holder.IconTreeItemHolder +import com.unnamed.b.atv.sample.holder.SelectableHeaderHolder +import com.unnamed.b.atv.view.AndroidTreeView + +/** + * Created by Bogdan Melnychuk on 2/12/15. + * Converted to Kolin by Kumar Shivang on 16/07/17 + */ +class TwoDScrollingFragment : Fragment() { + private var tView: AndroidTreeView? = null + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + val rootView = inflater.inflate(R.layout.fragment_selectable_nodes, container, false) + rootView.findViewById(R.id.status).visibility = View.GONE + val containerView = rootView.findViewById(R.id.container) as ViewGroup + + val root = TreeNode.root() + + val s1 = TreeNode(IconTreeItemHolder.IconTreeItem(R.string.ic_folder, "Folder with very long name ")).setViewHolder(SelectableHeaderHolder(activity)) + val s2 = TreeNode(IconTreeItemHolder.IconTreeItem(R.string.ic_folder, "Another folder with very long name")).setViewHolder(SelectableHeaderHolder(activity)) + + fillFolder(s1) + fillFolder(s2) + + root.addChildren(s1, s2) + + tView = AndroidTreeView(activity, root) + tView!!.setDefaultAnimation(true) + tView!!.setUse2dScroll(true) + tView!!.setDefaultContainerStyle(R.style.TreeNodeStyleCustom) + containerView.addView(tView!!.view) + + tView!!.expandAll() + + if (savedInstanceState != null) { + val state = savedInstanceState.getString("tState") + if (!TextUtils.isEmpty(state)) { + tView!!.restoreState(state!!) + } + } + return rootView + } + + private fun fillFolder(folder: TreeNode) { + var currentNode = folder + for (i in 0..9) { + val file = TreeNode(IconTreeItemHolder.IconTreeItem(R.string.ic_folder, NAME)).setViewHolder(SelectableHeaderHolder(activity)) + currentNode.addChild(file) + currentNode = file + } + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putString("tState", tView!!.saveState) + } + + companion object { + private val NAME = "Very long name for folder" + } +} diff --git a/app/src/main/java/com/unnamed/b/atv/sample/holder/ArrowExpandSelectableHeaderHolder.java b/app/src/main/java/com/unnamed/b/atv/sample/holder/ArrowExpandSelectableHeaderHolder.java deleted file mode 100644 index c58d084..0000000 --- a/app/src/main/java/com/unnamed/b/atv/sample/holder/ArrowExpandSelectableHeaderHolder.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.unnamed.b.atv.sample.holder; - -import android.content.Context; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.CheckBox; -import android.widget.CompoundButton; -import android.widget.TextView; - -import com.github.johnkil.print.PrintView; -import com.unnamed.b.atv.model.TreeNode; -import com.unnamed.b.atv.sample.R; - -/** - * Created by Bogdan Melnychuk on 2/15/15, modified by Szigeti Peter 2/2/16. - */ -public class ArrowExpandSelectableHeaderHolder extends TreeNode.BaseNodeViewHolder { - private TextView tvValue; - private PrintView arrowView; - private CheckBox nodeSelector; - - public ArrowExpandSelectableHeaderHolder(Context context) { - super(context); - } - - @Override - public View createNodeView(final TreeNode node, IconTreeItemHolder.IconTreeItem value) { - final LayoutInflater inflater = LayoutInflater.from(context); - final View view = inflater.inflate(R.layout.layout_selectable_header, null, false); - - tvValue = (TextView) view.findViewById(R.id.node_value); - tvValue.setText(value.text); - - final PrintView iconView = (PrintView) view.findViewById(R.id.icon); - iconView.setIconText(context.getResources().getString(value.icon)); - - arrowView = (PrintView) view.findViewById(R.id.arrow_icon); - arrowView.setPadding(20,10,10,10); - if (node.isLeaf()) { - arrowView.setVisibility(View.GONE); - } - arrowView.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - tView.toggleNode(node); - } - }); - - nodeSelector = (CheckBox) view.findViewById(R.id.node_selector); - nodeSelector.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - node.setSelected(isChecked); - for (TreeNode n : node.getChildren()) { - getTreeView().selectNode(n, isChecked); - } - } - }); - nodeSelector.setChecked(node.isSelected()); - - return view; - } - - @Override - public void toggle(boolean active) { - arrowView.setIconText(context.getResources().getString(active ? R.string.ic_keyboard_arrow_down : R.string.ic_keyboard_arrow_right)); - } - - @Override - public void toggleSelectionMode(boolean editModeEnabled) { - nodeSelector.setVisibility(editModeEnabled ? View.VISIBLE : View.GONE); - nodeSelector.setChecked(mNode.isSelected()); - } -} diff --git a/app/src/main/java/com/unnamed/b/atv/sample/holder/ArrowExpandSelectableHeaderHolder.kt b/app/src/main/java/com/unnamed/b/atv/sample/holder/ArrowExpandSelectableHeaderHolder.kt new file mode 100644 index 0000000..adbacf3 --- /dev/null +++ b/app/src/main/java/com/unnamed/b/atv/sample/holder/ArrowExpandSelectableHeaderHolder.kt @@ -0,0 +1,57 @@ +package com.unnamed.b.atv.sample.holder + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.widget.CheckBox +import android.widget.TextView +import com.github.johnkil.print.PrintView +import com.unnamed.b.atv.model.TreeNode +import com.unnamed.b.atv.sample.R + +/** + * Created by Bogdan Melnychuk on 2/15/15, modified by Szigeti Peter 2/2/16. + */ +class ArrowExpandSelectableHeaderHolder(context: Context) : TreeNode.BaseNodeViewHolder(context) { + private var tvValue: TextView? = null + private var arrowView: PrintView? = null + private var nodeSelector: CheckBox? = null + + override fun createNodeView(node: TreeNode, value: IconTreeItemHolder.IconTreeItem): View? { + val inflater = LayoutInflater.from(context) + val view = inflater.inflate(R.layout.layout_selectable_header, null, false) + + tvValue = view.findViewById(R.id.node_value) as TextView + tvValue!!.text = value.text + + val iconView = view.findViewById(R.id.icon) as PrintView + iconView.iconText = context.resources.getString(value.icon) + + arrowView = view.findViewById(R.id.arrow_icon) as PrintView + arrowView!!.setPadding(20, 10, 10, 10) + if (node.isLeaf) { + arrowView!!.visibility = View.GONE + } + arrowView!!.setOnClickListener { treeView!!.toggleNode(node) } + + nodeSelector = view.findViewById(R.id.node_selector) as CheckBox + nodeSelector!!.setOnCheckedChangeListener { buttonView, isChecked -> + node.isSelected = isChecked + for (n in node.getChildren()) { + treeView!!.selectNode(n, isChecked) + } + } + nodeSelector!!.isChecked = node.isSelected + + return view + } + + override fun toggle(active: Boolean) { + arrowView!!.iconText = context.resources.getString(if (active) R.string.ic_keyboard_arrow_down else R.string.ic_keyboard_arrow_right) + } + + override fun toggleSelectionMode(editModeEnabled: Boolean) { + nodeSelector!!.visibility = if (editModeEnabled) View.VISIBLE else View.GONE + nodeSelector!!.isChecked = mNode!!.isSelected + } +} diff --git a/app/src/main/java/com/unnamed/b/atv/sample/holder/HeaderHolder.java b/app/src/main/java/com/unnamed/b/atv/sample/holder/HeaderHolder.java deleted file mode 100644 index 23ea959..0000000 --- a/app/src/main/java/com/unnamed/b/atv/sample/holder/HeaderHolder.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.unnamed.b.atv.sample.holder; - -import android.content.Context; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.TextView; - -import com.github.johnkil.print.PrintView; -import com.unnamed.b.atv.model.TreeNode; -import com.unnamed.b.atv.sample.R; - -/** - * Created by Bogdan Melnychuk on 2/13/15. - */ -public class HeaderHolder extends TreeNode.BaseNodeViewHolder { - - private PrintView arrowView; - - public HeaderHolder(Context context) { - super(context); - } - - @Override - public View createNodeView(TreeNode node, IconTreeItemHolder.IconTreeItem value) { - final LayoutInflater inflater = LayoutInflater.from(context); - final View view = inflater.inflate(R.layout.layout_header_node, null, false); - TextView tvValue = (TextView) view.findViewById(R.id.node_value); - tvValue.setText(value.text); - - final PrintView iconView = (PrintView) view.findViewById(R.id.icon); - iconView.setIconText(context.getResources().getString(value.icon)); - - arrowView = (PrintView) view.findViewById(R.id.arrow_icon); - if (node.isLeaf()) { - arrowView.setVisibility(View.INVISIBLE); - } - - return view; - } - - @Override - public void toggle(boolean active) { - arrowView.setIconText(context.getResources().getString(active ? R.string.ic_keyboard_arrow_down : R.string.ic_keyboard_arrow_right)); - } - - -} diff --git a/app/src/main/java/com/unnamed/b/atv/sample/holder/HeaderHolder.kt b/app/src/main/java/com/unnamed/b/atv/sample/holder/HeaderHolder.kt new file mode 100644 index 0000000..65704e4 --- /dev/null +++ b/app/src/main/java/com/unnamed/b/atv/sample/holder/HeaderHolder.kt @@ -0,0 +1,41 @@ +package com.unnamed.b.atv.sample.holder + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.widget.TextView + +import com.github.johnkil.print.PrintView +import com.unnamed.b.atv.model.TreeNode +import com.unnamed.b.atv.sample.R + +/** + * Created by Bogdan Melnychuk on 2/13/15. + */ +class HeaderHolder(context: Context) : TreeNode.BaseNodeViewHolder(context) { + + private var arrowView: PrintView? = null + + override fun createNodeView(node: TreeNode, value: IconTreeItemHolder.IconTreeItem): View? { + val inflater = LayoutInflater.from(context) + val view = inflater.inflate(R.layout.layout_header_node, null, false) + val tvValue = view.findViewById(R.id.node_value) as TextView + tvValue.text = value.text + + val iconView = view.findViewById(R.id.icon) as PrintView + iconView.iconText = context.resources.getString(value.icon) + + arrowView = view.findViewById(R.id.arrow_icon) as PrintView + if (node.isLeaf) { + arrowView!!.visibility = View.INVISIBLE + } + + return view + } + + override fun toggle(active: Boolean) { + arrowView!!.iconText = context.resources.getString(if (active) R.string.ic_keyboard_arrow_down else R.string.ic_keyboard_arrow_right) + } + + +} diff --git a/app/src/main/java/com/unnamed/b/atv/sample/holder/IconTreeItemHolder.java b/app/src/main/java/com/unnamed/b/atv/sample/holder/IconTreeItemHolder.java deleted file mode 100644 index 0b0eaf8..0000000 --- a/app/src/main/java/com/unnamed/b/atv/sample/holder/IconTreeItemHolder.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.unnamed.b.atv.sample.holder; - -import android.content.Context; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.TextView; - -import com.github.johnkil.print.PrintView; -import com.unnamed.b.atv.model.TreeNode; -import com.unnamed.b.atv.sample.R; - -/** - * Created by Bogdan Melnychuk on 2/12/15. - */ -public class IconTreeItemHolder extends TreeNode.BaseNodeViewHolder { - private TextView tvValue; - private PrintView arrowView; - - public IconTreeItemHolder(Context context) { - super(context); - } - - @Override - public View createNodeView(final TreeNode node, IconTreeItem value) { - final LayoutInflater inflater = LayoutInflater.from(context); - final View view = inflater.inflate(R.layout.layout_icon_node, null, false); - tvValue = (TextView) view.findViewById(R.id.node_value); - tvValue.setText(value.text); - - final PrintView iconView = (PrintView) view.findViewById(R.id.icon); - iconView.setIconText(context.getResources().getString(value.icon)); - - arrowView = (PrintView) view.findViewById(R.id.arrow_icon); - - view.findViewById(R.id.btn_addFolder).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - TreeNode newFolder = new TreeNode(new IconTreeItemHolder.IconTreeItem(R.string.ic_folder, "New Folder")); - getTreeView().addNode(node, newFolder); - } - }); - - view.findViewById(R.id.btn_delete).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - getTreeView().removeNode(node); - } - }); - - //if My computer - if (node.getLevel() == 1) { - view.findViewById(R.id.btn_delete).setVisibility(View.GONE); - } - - return view; - } - - @Override - public void toggle(boolean active) { - arrowView.setIconText(context.getResources().getString(active ? R.string.ic_keyboard_arrow_down : R.string.ic_keyboard_arrow_right)); - } - - public static class IconTreeItem { - public int icon; - public String text; - - public IconTreeItem(int icon, String text) { - this.icon = icon; - this.text = text; - } - } -} diff --git a/app/src/main/java/com/unnamed/b/atv/sample/holder/IconTreeItemHolder.kt b/app/src/main/java/com/unnamed/b/atv/sample/holder/IconTreeItemHolder.kt new file mode 100644 index 0000000..e41fcb6 --- /dev/null +++ b/app/src/main/java/com/unnamed/b/atv/sample/holder/IconTreeItemHolder.kt @@ -0,0 +1,50 @@ +package com.unnamed.b.atv.sample.holder + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.widget.TextView + +import com.github.johnkil.print.PrintView +import com.unnamed.b.atv.model.TreeNode +import com.unnamed.b.atv.sample.R + +/** + * Created by Bogdan Melnychuk on 2/12/15. + */ +class IconTreeItemHolder(context: Context) : TreeNode.BaseNodeViewHolder(context) { + private var tvValue: TextView? = null + private var arrowView: PrintView? = null + + override fun createNodeView(node: TreeNode, value: IconTreeItem): View? { + val inflater = LayoutInflater.from(context) + val view = inflater.inflate(R.layout.layout_icon_node, null, false) + tvValue = view.findViewById(R.id.node_value) as TextView + tvValue!!.text = value.text + + val iconView = view.findViewById(R.id.icon) as PrintView + iconView.iconText = context.resources.getString(value.icon) + + arrowView = view.findViewById(R.id.arrow_icon) as PrintView + + view.findViewById(R.id.btn_addFolder).setOnClickListener { + val newFolder = TreeNode(IconTreeItemHolder.IconTreeItem(R.string.ic_folder, "New Folder")) + treeView!!.addNode(node, newFolder) + } + + view.findViewById(R.id.btn_delete).setOnClickListener { treeView!!.removeNode(node) } + + //if My computer + if (node.level == 1) { + view.findViewById(R.id.btn_delete).visibility = View.GONE + } + + return view + } + + override fun toggle(active: Boolean) { + arrowView!!.iconText = context.resources.getString(if (active) R.string.ic_keyboard_arrow_down else R.string.ic_keyboard_arrow_right) + } + + class IconTreeItem(var icon: Int, var text: String) +} diff --git a/app/src/main/java/com/unnamed/b/atv/sample/holder/PlaceHolderHolder.java b/app/src/main/java/com/unnamed/b/atv/sample/holder/PlaceHolderHolder.java deleted file mode 100644 index 6e613bb..0000000 --- a/app/src/main/java/com/unnamed/b/atv/sample/holder/PlaceHolderHolder.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.unnamed.b.atv.sample.holder; - -import android.content.Context; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.TextView; - -import com.github.johnkil.print.PrintView; -import com.unnamed.b.atv.model.TreeNode; -import com.unnamed.b.atv.sample.R; - -import java.util.Random; - -/** - * Created by Bogdan Melnychuk on 2/13/15. - */ -public class PlaceHolderHolder extends TreeNode.BaseNodeViewHolder { - - - public PlaceHolderHolder(Context context) { - super(context); - } - - @Override - public View createNodeView(TreeNode node, PlaceItem value) { - final LayoutInflater inflater = LayoutInflater.from(context); - final View view = inflater.inflate(R.layout.layout_place_node, null, false); - - - TextView placeName = (TextView) view.findViewById(R.id.place_name); - placeName.setText(value.name); - - Random r = new Random(); - boolean like = r.nextBoolean(); - - PrintView likeView = (PrintView) view.findViewById(R.id.like); - likeView.setIconText(context.getString(like ? R.string.ic_thumbs_up : R.string.ic_thumbs_down)); - return view; - } - - @Override - public void toggle(boolean active) { - } - - - public static class PlaceItem { - public String name; - - public PlaceItem(String name) { - this.name = name; - } - // rest will be hardcoded - } - -} diff --git a/app/src/main/java/com/unnamed/b/atv/sample/holder/PlaceHolderHolder.kt b/app/src/main/java/com/unnamed/b/atv/sample/holder/PlaceHolderHolder.kt new file mode 100644 index 0000000..7c020a1 --- /dev/null +++ b/app/src/main/java/com/unnamed/b/atv/sample/holder/PlaceHolderHolder.kt @@ -0,0 +1,38 @@ +package com.unnamed.b.atv.sample.holder + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.widget.TextView +import com.github.johnkil.print.PrintView +import com.unnamed.b.atv.model.TreeNode +import com.unnamed.b.atv.sample.R +import java.util.* + +/** + * Created by Bogdan Melnychuk on 2/13/15. + */ +class PlaceHolderHolder(context: Context) : TreeNode.BaseNodeViewHolder(context) { + + override fun createNodeView(node: TreeNode, value: PlaceItem): View? { + val inflater = LayoutInflater.from(context) + val view = inflater.inflate(R.layout.layout_place_node, null, false) + + + val placeName = view.findViewById(R.id.place_name) as TextView + placeName.text = value.name + + val r = Random() + val like = r.nextBoolean() + + val likeView = view.findViewById(R.id.like) as PrintView + likeView.iconText = context.getString(if (like) R.string.ic_thumbs_up else R.string.ic_thumbs_down) + return view + } + + override fun toggle(active: Boolean) {} + + + class PlaceItem(var name: String)// rest will be hardcoded + +} diff --git a/app/src/main/java/com/unnamed/b/atv/sample/holder/ProfileHolder.java b/app/src/main/java/com/unnamed/b/atv/sample/holder/ProfileHolder.java deleted file mode 100644 index 42b05be..0000000 --- a/app/src/main/java/com/unnamed/b/atv/sample/holder/ProfileHolder.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.unnamed.b.atv.sample.holder; - -import android.content.Context; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.TextView; - -import com.github.johnkil.print.PrintView; -import com.unnamed.b.atv.model.TreeNode; -import com.unnamed.b.atv.sample.R; - -/** - * Created by Bogdan Melnychuk on 2/13/15. - */ -public class ProfileHolder extends TreeNode.BaseNodeViewHolder { - - - public ProfileHolder(Context context) { - super(context); - } - - @Override - public View createNodeView(TreeNode node, IconTreeItemHolder.IconTreeItem value) { - final LayoutInflater inflater = LayoutInflater.from(context); - final View view = inflater.inflate(R.layout.layout_profile_node, null, false); - TextView tvValue = (TextView) view.findViewById(R.id.node_value); - tvValue.setText(value.text); - - final PrintView iconView = (PrintView) view.findViewById(R.id.icon); - iconView.setIconText(context.getResources().getString(value.icon)); - - return view; - } - - @Override - public void toggle(boolean active) { - } - - @Override - public int getContainerStyle() { - return R.style.TreeNodeStyleCustom; - } -} diff --git a/app/src/main/java/com/unnamed/b/atv/sample/holder/ProfileHolder.kt b/app/src/main/java/com/unnamed/b/atv/sample/holder/ProfileHolder.kt new file mode 100644 index 0000000..e1fd951 --- /dev/null +++ b/app/src/main/java/com/unnamed/b/atv/sample/holder/ProfileHolder.kt @@ -0,0 +1,36 @@ +package com.unnamed.b.atv.sample.holder + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.widget.TextView + +import com.github.johnkil.print.PrintView +import com.unnamed.b.atv.model.TreeNode +import com.unnamed.b.atv.sample.R + +/** + * Created by Bogdan Melnychuk on 2/13/15. + */ +class ProfileHolder(context: Context) : TreeNode.BaseNodeViewHolder(context) { + + override fun createNodeView(node: TreeNode, value: IconTreeItemHolder.IconTreeItem): View? { + val inflater = LayoutInflater.from(context) + val view = inflater.inflate(R.layout.layout_profile_node, null, false) + val tvValue = view.findViewById(R.id.node_value) as TextView + tvValue.text = value.text + + val iconView = view.findViewById(R.id.icon) as PrintView + iconView.iconText = context.resources.getString(value.icon) + + return view + } + + override fun toggle(active: Boolean) {} + + override var containerStyle: Int + get() = R.style.TreeNodeStyleCustom + set(value: Int) { + super.containerStyle = value + } +} diff --git a/app/src/main/java/com/unnamed/b/atv/sample/holder/SelectableHeaderHolder.java b/app/src/main/java/com/unnamed/b/atv/sample/holder/SelectableHeaderHolder.java deleted file mode 100644 index f0c912c..0000000 --- a/app/src/main/java/com/unnamed/b/atv/sample/holder/SelectableHeaderHolder.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.unnamed.b.atv.sample.holder; - -import android.content.Context; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.CheckBox; -import android.widget.CompoundButton; -import android.widget.TextView; - -import com.github.johnkil.print.PrintView; -import com.unnamed.b.atv.model.TreeNode; -import com.unnamed.b.atv.sample.R; - -/** - * Created by Bogdan Melnychuk on 2/15/15. - */ -public class SelectableHeaderHolder extends TreeNode.BaseNodeViewHolder { - private TextView tvValue; - private PrintView arrowView; - private CheckBox nodeSelector; - - public SelectableHeaderHolder(Context context) { - super(context); - } - - @Override - public View createNodeView(final TreeNode node, IconTreeItemHolder.IconTreeItem value) { - final LayoutInflater inflater = LayoutInflater.from(context); - final View view = inflater.inflate(R.layout.layout_selectable_header, null, false); - - tvValue = (TextView) view.findViewById(R.id.node_value); - tvValue.setText(value.text); - - final PrintView iconView = (PrintView) view.findViewById(R.id.icon); - iconView.setIconText(context.getResources().getString(value.icon)); - - arrowView = (PrintView) view.findViewById(R.id.arrow_icon); - if (node.isLeaf()) { - arrowView.setVisibility(View.GONE); - } - - nodeSelector = (CheckBox) view.findViewById(R.id.node_selector); - nodeSelector.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - node.setSelected(isChecked); - for (TreeNode n : node.getChildren()) { - getTreeView().selectNode(n, isChecked); - } - } - }); - nodeSelector.setChecked(node.isSelected()); - - return view; - } - - @Override - public void toggle(boolean active) { - arrowView.setIconText(context.getResources().getString(active ? R.string.ic_keyboard_arrow_down : R.string.ic_keyboard_arrow_right)); - } - - @Override - public void toggleSelectionMode(boolean editModeEnabled) { - nodeSelector.setVisibility(editModeEnabled ? View.VISIBLE : View.GONE); - nodeSelector.setChecked(mNode.isSelected()); - } -} diff --git a/app/src/main/java/com/unnamed/b/atv/sample/holder/SelectableHeaderHolder.kt b/app/src/main/java/com/unnamed/b/atv/sample/holder/SelectableHeaderHolder.kt new file mode 100644 index 0000000..577a41e --- /dev/null +++ b/app/src/main/java/com/unnamed/b/atv/sample/holder/SelectableHeaderHolder.kt @@ -0,0 +1,55 @@ +package com.unnamed.b.atv.sample.holder + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.widget.CheckBox +import android.widget.TextView +import com.github.johnkil.print.PrintView +import com.unnamed.b.atv.model.TreeNode +import com.unnamed.b.atv.sample.R + +/** + * Created by Bogdan Melnychuk on 2/15/15. + */ +class SelectableHeaderHolder(context: Context) : TreeNode.BaseNodeViewHolder(context) { + private var tvValue: TextView? = null + private var arrowView: PrintView? = null + private var nodeSelector: CheckBox? = null + + override fun createNodeView(node: TreeNode, value: IconTreeItemHolder.IconTreeItem): View? { + val inflater = LayoutInflater.from(context) + val view = inflater.inflate(R.layout.layout_selectable_header, null, false) + + tvValue = view.findViewById(R.id.node_value) as TextView + tvValue!!.text = value.text + + val iconView = view.findViewById(R.id.icon) as PrintView + iconView.iconText = context.resources.getString(value.icon) + + arrowView = view.findViewById(R.id.arrow_icon) as PrintView + if (node.isLeaf) { + arrowView!!.visibility = View.GONE + } + + nodeSelector = view.findViewById(R.id.node_selector) as CheckBox + nodeSelector!!.setOnCheckedChangeListener { buttonView, isChecked -> + node.isSelected = isChecked + for (n in node.getChildren()) { + treeView!!.selectNode(n, isChecked) + } + } + nodeSelector!!.isChecked = node.isSelected + + return view + } + + override fun toggle(active: Boolean) { + arrowView!!.iconText = context.resources.getString(if (active) R.string.ic_keyboard_arrow_down else R.string.ic_keyboard_arrow_right) + } + + override fun toggleSelectionMode(editModeEnabled: Boolean) { + nodeSelector!!.visibility = if (editModeEnabled) View.VISIBLE else View.GONE + nodeSelector!!.isChecked = mNode!!.isSelected + } +} diff --git a/app/src/main/java/com/unnamed/b/atv/sample/holder/SelectableItemHolder.java b/app/src/main/java/com/unnamed/b/atv/sample/holder/SelectableItemHolder.java deleted file mode 100644 index b5c0452..0000000 --- a/app/src/main/java/com/unnamed/b/atv/sample/holder/SelectableItemHolder.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.unnamed.b.atv.sample.holder; - -import android.content.Context; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.CheckBox; -import android.widget.CompoundButton; -import android.widget.TextView; - -import com.unnamed.b.atv.model.TreeNode; -import com.unnamed.b.atv.sample.R; - -/** - * Created by Bogdan Melnychuk on 2/15/15. - */ -public class SelectableItemHolder extends TreeNode.BaseNodeViewHolder { - private TextView tvValue; - private CheckBox nodeSelector; - - public SelectableItemHolder(Context context) { - super(context); - } - - @Override - public View createNodeView(final TreeNode node, String value) { - final LayoutInflater inflater = LayoutInflater.from(context); - final View view = inflater.inflate(R.layout.layout_selectable_item, null, false); - - tvValue = (TextView) view.findViewById(R.id.node_value); - tvValue.setText(value); - - - nodeSelector = (CheckBox) view.findViewById(R.id.node_selector); - nodeSelector.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - node.setSelected(isChecked); - } - }); - nodeSelector.setChecked(node.isSelected()); - - if (node.isLastChild()) { - view.findViewById(R.id.bot_line).setVisibility(View.INVISIBLE); - } - - return view; - } - - - @Override - public void toggleSelectionMode(boolean editModeEnabled) { - nodeSelector.setVisibility(editModeEnabled ? View.VISIBLE : View.GONE); - nodeSelector.setChecked(mNode.isSelected()); - } -} diff --git a/app/src/main/java/com/unnamed/b/atv/sample/holder/SelectableItemHolder.kt b/app/src/main/java/com/unnamed/b/atv/sample/holder/SelectableItemHolder.kt new file mode 100644 index 0000000..2e28427 --- /dev/null +++ b/app/src/main/java/com/unnamed/b/atv/sample/holder/SelectableItemHolder.kt @@ -0,0 +1,42 @@ +package com.unnamed.b.atv.sample.holder + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.widget.CheckBox +import android.widget.TextView +import com.unnamed.b.atv.model.TreeNode +import com.unnamed.b.atv.sample.R + +/** + * Created by Bogdan Melnychuk on 2/15/15. + */ +class SelectableItemHolder(context: Context) : TreeNode.BaseNodeViewHolder(context) { + private var tvValue: TextView? = null + private var nodeSelector: CheckBox? = null + + override fun createNodeView(node: TreeNode, value: String): View? { + val inflater = LayoutInflater.from(context) + val view = inflater.inflate(R.layout.layout_selectable_item, null, false) + + tvValue = view.findViewById(R.id.node_value) as TextView + tvValue!!.text = value + + + nodeSelector = view.findViewById(R.id.node_selector) as CheckBox + nodeSelector!!.setOnCheckedChangeListener { buttonView, isChecked -> node.isSelected = isChecked } + nodeSelector!!.isChecked = node.isSelected + + if (node.isLastChild) { + view.findViewById(R.id.bot_line).visibility = View.INVISIBLE + } + + return view + } + + + override fun toggleSelectionMode(editModeEnabled: Boolean) { + nodeSelector!!.visibility = if (editModeEnabled) View.VISIBLE else View.GONE + nodeSelector!!.isChecked = mNode!!.isSelected + } +} diff --git a/app/src/main/java/com/unnamed/b/atv/sample/holder/SocialViewHolder.java b/app/src/main/java/com/unnamed/b/atv/sample/holder/SocialViewHolder.java deleted file mode 100644 index 1740801..0000000 --- a/app/src/main/java/com/unnamed/b/atv/sample/holder/SocialViewHolder.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.unnamed.b.atv.sample.holder; - -import android.content.Context; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.TextView; - -import com.github.johnkil.print.PrintView; -import com.unnamed.b.atv.model.TreeNode; -import com.unnamed.b.atv.sample.R; - -import java.util.Random; - -/** - * Created by Bogdan Melnychuk on 2/13/15. - */ -public class SocialViewHolder extends TreeNode.BaseNodeViewHolder { - - private static final String[] NAMES = new String[]{"Bruce Wayne", "Clark Kent", "Barry Allen", "Hal Jordan"}; - - public SocialViewHolder(Context context) { - super(context); - } - - @Override - public View createNodeView(TreeNode node, SocialItem value) { - final LayoutInflater inflater = LayoutInflater.from(context); - final View view = inflater.inflate(R.layout.layout_social_node, null, false); - - final PrintView iconView = (PrintView) view.findViewById(R.id.icon); - iconView.setIconText(context.getResources().getString(value.icon)); - - TextView connectionsLabel = (TextView) view.findViewById(R.id.connections); - Random r = new Random(); - connectionsLabel.setText(r.nextInt(150) + " connections"); - - TextView userNameLabel = (TextView) view.findViewById(R.id.username); - userNameLabel.setText(NAMES[r.nextInt(4)]); - - TextView sizeText = (TextView) view.findViewById(R.id.size); - sizeText.setText(r.nextInt(10) + " items"); - - return view; - } - - @Override - public void toggle(boolean active) { - } - - - public static class SocialItem { - public int icon; - - public SocialItem(int icon) { - this.icon = icon; - } - // rest will be hardcoded - } - -} diff --git a/app/src/main/java/com/unnamed/b/atv/sample/holder/SocialViewHolder.kt b/app/src/main/java/com/unnamed/b/atv/sample/holder/SocialViewHolder.kt new file mode 100644 index 0000000..ce369be --- /dev/null +++ b/app/src/main/java/com/unnamed/b/atv/sample/holder/SocialViewHolder.kt @@ -0,0 +1,47 @@ +package com.unnamed.b.atv.sample.holder + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.widget.TextView +import com.github.johnkil.print.PrintView +import com.unnamed.b.atv.model.TreeNode +import com.unnamed.b.atv.sample.R +import java.util.* + +/** + * Created by Bogdan Melnychuk on 2/13/15. + */ +class SocialViewHolder(context: Context) : TreeNode.BaseNodeViewHolder(context) { + + override fun createNodeView(node: TreeNode, value: SocialItem): View? { + val inflater = LayoutInflater.from(context) + val view = inflater.inflate(R.layout.layout_social_node, null, false) + + val iconView = view.findViewById(R.id.icon) as PrintView + iconView.iconText = context.resources.getString(value.icon) + + val connectionsLabel = view.findViewById(R.id.connections) as TextView + val r = Random() + connectionsLabel.text = r.nextInt(150).toString() + " connections" + + val userNameLabel = view.findViewById(R.id.username) as TextView + userNameLabel.text = NAMES[r.nextInt(4)] + + val sizeText = view.findViewById(R.id.size) as TextView + sizeText.text = r.nextInt(10).toString() + " items" + + return view + } + + override fun toggle(active: Boolean) {} + + + class SocialItem(var icon: Int)// rest will be hardcoded + + companion object { + + private val NAMES = arrayOf("Bruce Wayne", "Clark Kent", "Barry Allen", "Hal Jordan") + } + +} diff --git a/build.gradle b/build.gradle index ebf706b..01a6ae9 100644 --- a/build.gradle +++ b/build.gradle @@ -1,11 +1,13 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { + ext.kotlin_version = '1.1.3-2' repositories { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:1.0.0' + classpath 'com.android.tools.build:gradle:2.3.3' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/gradle.properties b/gradle.properties index ddb8205..d467ea5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -22,9 +22,9 @@ VERSION_CODE=11 ANDROID_BUILD_MIN_SDK_VERSION=11 -ANDROID_BUILD_TARGET_SDK_VERSION=21 -ANDROID_BUILD_SDK_VERSION=21 -ANDROID_BUILD_TOOLS_VERSION=21.1.2 +ANDROID_BUILD_TARGET_SDK_VERSION=25 +ANDROID_BUILD_SDK_VERSION=25 +ANDROID_BUILD_TOOLS_VERSION=25.0.3 GROUP=com.github.bmelnychuk POM_DESCRIPTION=Tree View implementation for android diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 0c71e76..e75f47b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Wed Apr 10 15:27:10 PDT 2013 +#Sun Jul 16 01:41:43 IST 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip diff --git a/library/build.gradle b/library/build.gradle index 92ecb9f..7cfd8db 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -1,4 +1,5 @@ apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' android { compileSdkVersion Integer.parseInt(project.ANDROID_BUILD_SDK_VERSION) @@ -19,7 +20,8 @@ android { dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) - compile 'com.android.support:appcompat-v7:21.0.3' + compile 'com.android.support:appcompat-v7:25.3.1' + compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version" } apply from: '../maven_push.gradle' diff --git a/library/src/main/java/com/unnamed/b/atv/holder/SimpleViewHolder.java b/library/src/main/java/com/unnamed/b/atv/holder/SimpleViewHolder.java deleted file mode 100644 index 1f73b5f..0000000 --- a/library/src/main/java/com/unnamed/b/atv/holder/SimpleViewHolder.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.unnamed.b.atv.holder; - -import android.content.Context; -import android.view.View; -import android.widget.TextView; - -import com.unnamed.b.atv.model.TreeNode; - -/** - * Created by Bogdan Melnychuk on 2/11/15. - */ -public class SimpleViewHolder extends TreeNode.BaseNodeViewHolder { - - public SimpleViewHolder(Context context) { - super(context); - } - - @Override - public View createNodeView(TreeNode node, Object value) { - final TextView tv = new TextView(context); - tv.setText(String.valueOf(value)); - return tv; - } - - @Override - public void toggle(boolean active) { - - } -} diff --git a/library/src/main/java/com/unnamed/b/atv/holder/SimpleViewHolder.kt b/library/src/main/java/com/unnamed/b/atv/holder/SimpleViewHolder.kt new file mode 100644 index 0000000..919a119 --- /dev/null +++ b/library/src/main/java/com/unnamed/b/atv/holder/SimpleViewHolder.kt @@ -0,0 +1,24 @@ +package com.unnamed.b.atv.holder + +import android.content.Context +import android.view.View +import android.widget.TextView + +import com.unnamed.b.atv.model.TreeNode + +/** + * Created by Bogdan Melnychuk on 2/11/15. + * Converted to Kolin by Kumar Shivang on 16/07/17 + */ +class SimpleViewHolder(context: Context) : TreeNode.BaseNodeViewHolder(context) { + + override fun createNodeView(node: TreeNode, value: Any): View { + val tv = TextView(context) + tv.text = value.toString() + return tv + } + + override fun toggle(active: Boolean) { + + } +} diff --git a/library/src/main/java/com/unnamed/b/atv/model/TreeNode.java b/library/src/main/java/com/unnamed/b/atv/model/TreeNode.java deleted file mode 100644 index dbf33f8..0000000 --- a/library/src/main/java/com/unnamed/b/atv/model/TreeNode.java +++ /dev/null @@ -1,284 +0,0 @@ -package com.unnamed.b.atv.model; - -import android.content.Context; -import android.view.View; -import android.view.ViewGroup; - -import com.unnamed.b.atv.R; -import com.unnamed.b.atv.view.AndroidTreeView; -import com.unnamed.b.atv.view.TreeNodeWrapperView; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -/** - * Created by Bogdan Melnychuk on 2/10/15. - */ -public class TreeNode { - public static final String NODES_ID_SEPARATOR = ":"; - - private int mId; - private int mLastId; - private TreeNode mParent; - private boolean mSelected; - private boolean mSelectable = true; - private final List children; - private BaseNodeViewHolder mViewHolder; - private TreeNodeClickListener mClickListener; - private TreeNodeLongClickListener mLongClickListener; - private Object mValue; - private boolean mExpanded; - - public static TreeNode root() { - TreeNode root = new TreeNode(null); - root.setSelectable(false); - return root; - } - - private int generateId() { - return ++mLastId; - } - - public TreeNode(Object value) { - children = new ArrayList<>(); - mValue = value; - } - - public TreeNode addChild(TreeNode childNode) { - childNode.mParent = this; - childNode.mId = generateId(); - children.add(childNode); - return this; - } - - public TreeNode addChildren(TreeNode... nodes) { - for (TreeNode n : nodes) { - addChild(n); - } - return this; - } - - public TreeNode addChildren(Collection nodes) { - for (TreeNode n : nodes) { - addChild(n); - } - return this; - } - - public int deleteChild(TreeNode child) { - for (int i = 0; i < children.size(); i++) { - if (child.mId == children.get(i).mId) { - children.remove(i); - return i; - } - } - return -1; - } - - public List getChildren() { - return Collections.unmodifiableList(children); - } - - public int size() { - return children.size(); - } - - public TreeNode getParent() { - return mParent; - } - - public int getId() { - return mId; - } - - public boolean isLeaf() { - return size() == 0; - } - - public Object getValue() { - return mValue; - } - - public boolean isExpanded() { - return mExpanded; - } - - public TreeNode setExpanded(boolean expanded) { - mExpanded = expanded; - return this; - } - - public void setSelected(boolean selected) { - mSelected = selected; - } - - public boolean isSelected() { - return mSelectable && mSelected; - } - - public void setSelectable(boolean selectable) { - mSelectable = selectable; - } - - public boolean isSelectable() { - return mSelectable; - } - - public String getPath() { - final StringBuilder path = new StringBuilder(); - TreeNode node = this; - while (node.mParent != null) { - path.append(node.getId()); - node = node.mParent; - if (node.mParent != null) { - path.append(NODES_ID_SEPARATOR); - } - } - return path.toString(); - } - - - public int getLevel() { - int level = 0; - TreeNode root = this; - while (root.mParent != null) { - root = root.mParent; - level++; - } - return level; - } - - public boolean isLastChild() { - if (!isRoot()) { - int parentSize = mParent.children.size(); - if (parentSize > 0) { - final List parentChildren = mParent.children; - return parentChildren.get(parentSize - 1).mId == mId; - } - } - return false; - } - - public TreeNode setViewHolder(BaseNodeViewHolder viewHolder) { - mViewHolder = viewHolder; - if (viewHolder != null) { - viewHolder.mNode = this; - } - return this; - } - - public TreeNode setClickListener(TreeNodeClickListener listener) { - mClickListener = listener; - return this; - } - - public TreeNodeClickListener getClickListener() { - return this.mClickListener; - } - - public TreeNode setLongClickListener(TreeNodeLongClickListener listener) { - mLongClickListener = listener; - return this; - } - - public TreeNodeLongClickListener getLongClickListener() { - return mLongClickListener; - } - - public BaseNodeViewHolder getViewHolder() { - return mViewHolder; - } - - public boolean isFirstChild() { - if (!isRoot()) { - List parentChildren = mParent.children; - return parentChildren.get(0).mId == mId; - } - return false; - } - - public boolean isRoot() { - return mParent == null; - } - - public TreeNode getRoot() { - TreeNode root = this; - while (root.mParent != null) { - root = root.mParent; - } - return root; - } - - public interface TreeNodeClickListener { - void onClick(TreeNode node, Object value); - } - - public interface TreeNodeLongClickListener { - boolean onLongClick(TreeNode node, Object value); - } - - public static abstract class BaseNodeViewHolder { - protected AndroidTreeView tView; - protected TreeNode mNode; - private View mView; - protected int containerStyle; - protected Context context; - - public BaseNodeViewHolder(Context context) { - this.context = context; - } - - public View getView() { - if (mView != null) { - return mView; - } - final View nodeView = getNodeView(); - final TreeNodeWrapperView nodeWrapperView = new TreeNodeWrapperView(nodeView.getContext(), getContainerStyle()); - nodeWrapperView.insertNodeView(nodeView); - mView = nodeWrapperView; - - return mView; - } - - public void setTreeViev(AndroidTreeView treeViev) { - this.tView = treeViev; - } - - public AndroidTreeView getTreeView() { - return tView; - } - - public void setContainerStyle(int style) { - containerStyle = style; - } - - public View getNodeView() { - return createNodeView(mNode, (E) mNode.getValue()); - } - - public ViewGroup getNodeItemsView() { - return (ViewGroup) getView().findViewById(R.id.node_items); - } - - public boolean isInitialized() { - return mView != null; - } - - public int getContainerStyle() { - return containerStyle; - } - - - public abstract View createNodeView(TreeNode node, E value); - - public void toggle(boolean active) { - // empty - } - - public void toggleSelectionMode(boolean editModeEnabled) { - // empty - } - } -} diff --git a/library/src/main/java/com/unnamed/b/atv/model/TreeNode.kt b/library/src/main/java/com/unnamed/b/atv/model/TreeNode.kt new file mode 100644 index 0000000..b4d7510 --- /dev/null +++ b/library/src/main/java/com/unnamed/b/atv/model/TreeNode.kt @@ -0,0 +1,241 @@ +package com.unnamed.b.atv.model + +import android.content.Context +import android.view.View +import android.view.ViewGroup +import com.unnamed.b.atv.R +import com.unnamed.b.atv.view.AndroidTreeView +import com.unnamed.b.atv.view.TreeNodeWrapperView +import java.util.* + +/** + * Created by Bogdan Melnychuk on 2/10/15. + * Converted to Kolin by Kumar Shivang on 16/07/17 + */ +class TreeNode(val value: Any?) { + + var id: Int = 0 + private set + private var mLastId: Int = 0 + var parent: TreeNode? = null + private set + var isSelected: Boolean = false + get() = isSelectable && field + var isSelectable = true + private val children: MutableList + private var mViewHolder: BaseNodeViewHolder<*>? = null + private var mClickListener: TreeNodeClickListener? = null + private var mLongClickListener: TreeNodeLongClickListener? = null + private var mExpanded: Boolean = false + + private fun generateId(): Int { + return ++mLastId + } + + init { + children = ArrayList() + } + + fun addChild(childNode: TreeNode): TreeNode { + childNode.parent = this + childNode.id = generateId() + children.add(childNode) + return this + } + + fun addChildren(vararg nodes: TreeNode): TreeNode { + for (n in nodes) { + addChild(n) + } + return this + } + + fun addChildren(nodes: Collection): TreeNode { + for (n in nodes) { + addChild(n) + } + return this + } + + fun deleteChild(child: TreeNode): Int { + for (i in children.indices) { + if (child.id == children[i].id) { + children.removeAt(i) + return i + } + } + return -1 + } + + fun getChildren(): List { + return Collections.unmodifiableList(children) + } + + fun size(): Int { + return children.size + } + + val isLeaf: Boolean + get() = size() == 0 + + fun isExpanded(): Boolean { + return mExpanded + } + + fun setExpanded(expanded: Boolean): TreeNode { + mExpanded = expanded + return this + } + + val path: String + get() { + val path = StringBuilder() + var node = this + while (node.parent != null) { + path.append(node.id) + node = node.parent!! + if (node.parent != null) { + path.append(NODES_ID_SEPARATOR) + } + } + return path.toString() + } + + + val level: Int + get() { + var level = 0 + var root = this + while (root.parent != null) { + root = root.parent!! + level++ + } + return level + } + + val isLastChild: Boolean + get() { + if (!isRoot) { + val parentSize = parent!!.children.size + if (parentSize > 0) { + val parentChildren = parent!!.children + return parentChildren[parentSize - 1].id == id + } + } + return false + } + + fun setViewHolder(viewHolder: BaseNodeViewHolder<*>?): TreeNode { + mViewHolder = viewHolder + if (viewHolder != null) { + viewHolder.mNode = this + } + return this + } + + fun setClickListener(listener: TreeNodeClickListener): TreeNode { + mClickListener = listener + return this + } + + fun getClickListener(): TreeNodeClickListener? { + return this.mClickListener + } + + fun setLongClickListener(listener: TreeNodeLongClickListener): TreeNode { + mLongClickListener = listener + return this + } + + fun getLongClickListener(): TreeNodeLongClickListener? { + return mLongClickListener + } + + fun getViewHolder(): BaseNodeViewHolder<*>? { + return mViewHolder + } + + val isFirstChild: Boolean + get() { + if (!isRoot) { + val parentChildren = parent!!.children + return parentChildren[0].id == id + } + return false + } + + val isRoot: Boolean + get() = parent == null + + val root: TreeNode + get() { + var root = this + while (root.parent != null) { + root = root.parent!! + } + return root + } + + interface TreeNodeClickListener { + fun onClick(node: TreeNode, value: Any) + } + + interface TreeNodeLongClickListener { + fun onLongClick(node: TreeNode, value: Any): Boolean + } + + abstract class BaseNodeViewHolder(protected var context: Context) { + var treeView: AndroidTreeView? = null + protected set + var mNode: TreeNode? = null + private var mView: View? = null + open var containerStyle: Int = 0 + + val view: View + get() { + if (mView != null) { + return mView!! + } + val nodeView = nodeView + val nodeWrapperView = TreeNodeWrapperView(nodeView!!.context, containerStyle) + nodeWrapperView.insertNodeView(nodeView) + mView = nodeWrapperView + + return mView!! + } + + fun setTreeViev(treeViev: AndroidTreeView) { + this.treeView = treeViev + } + + val nodeView: View? + get() = createNodeView(mNode!!, mNode!!.value as E) + + open val nodeItemsView: ViewGroup + get() = view.findViewById(R.id.node_items) as ViewGroup + + val isInitialized: Boolean + get() = mView != null + + + abstract fun createNodeView(node: TreeNode, value: E): View? + + open fun toggle(active: Boolean) { + // empty + } + + open fun toggleSelectionMode(editModeEnabled: Boolean) { + // empty + } + } + + companion object { + val NODES_ID_SEPARATOR = ":" + + fun root(): TreeNode { + val root = TreeNode(null) + root.isSelectable = false + return root + } + } +} diff --git a/library/src/main/java/com/unnamed/b/atv/view/AndroidTreeView.java b/library/src/main/java/com/unnamed/b/atv/view/AndroidTreeView.java deleted file mode 100644 index 222a43a..0000000 --- a/library/src/main/java/com/unnamed/b/atv/view/AndroidTreeView.java +++ /dev/null @@ -1,488 +0,0 @@ -package com.unnamed.b.atv.view; - -import android.content.Context; -import android.text.TextUtils; -import android.view.ContextThemeWrapper; -import android.view.View; -import android.view.ViewGroup; -import android.view.animation.Animation; -import android.view.animation.Transformation; -import android.widget.LinearLayout; -import android.widget.ScrollView; - -import com.unnamed.b.atv.R; -import com.unnamed.b.atv.holder.SimpleViewHolder; -import com.unnamed.b.atv.model.TreeNode; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * Created by Bogdan Melnychuk on 2/10/15. - */ -public class AndroidTreeView { - private static final String NODES_PATH_SEPARATOR = ";"; - - protected TreeNode mRoot; - private Context mContext; - private boolean applyForRoot; - private int containerStyle = 0; - private Class defaultViewHolderClass = SimpleViewHolder.class; - private TreeNode.TreeNodeClickListener nodeClickListener; - private TreeNode.TreeNodeLongClickListener nodeLongClickListener; - private boolean mSelectionModeEnabled; - private boolean mUseDefaultAnimation = false; - private boolean use2dScroll = false; - private boolean enableAutoToggle = true; - - public AndroidTreeView(Context context) { - mContext = context; - } - - public void setRoot(TreeNode mRoot) { - this.mRoot = mRoot; - } - - public AndroidTreeView(Context context, TreeNode root) { - mRoot = root; - mContext = context; - } - - public void setDefaultAnimation(boolean defaultAnimation) { - this.mUseDefaultAnimation = defaultAnimation; - } - - public void setDefaultContainerStyle(int style) { - setDefaultContainerStyle(style, false); - } - - public void setDefaultContainerStyle(int style, boolean applyForRoot) { - containerStyle = style; - this.applyForRoot = applyForRoot; - } - - public void setUse2dScroll(boolean use2dScroll) { - this.use2dScroll = use2dScroll; - } - - public boolean is2dScrollEnabled() { - return use2dScroll; - } - - public void setUseAutoToggle(boolean enableAutoToggle) { - this.enableAutoToggle = enableAutoToggle; - } - - public boolean isAutoToggleEnabled() { - return enableAutoToggle; - } - - public void setDefaultViewHolder(Class viewHolder) { - defaultViewHolderClass = viewHolder; - } - - public void setDefaultNodeClickListener(TreeNode.TreeNodeClickListener listener) { - nodeClickListener = listener; - } - - public void setDefaultNodeLongClickListener(TreeNode.TreeNodeLongClickListener listener) { - nodeLongClickListener = listener; - } - - public void expandAll() { - expandNode(mRoot, true); - } - - public void collapseAll() { - for (TreeNode n : mRoot.getChildren()) { - collapseNode(n, true); - } - } - - - public View getView(int style) { - final ViewGroup view; - if (style > 0) { - ContextThemeWrapper newContext = new ContextThemeWrapper(mContext, style); - view = use2dScroll ? new TwoDScrollView(newContext) : new ScrollView(newContext); - } else { - view = use2dScroll ? new TwoDScrollView(mContext) : new ScrollView(mContext); - } - - Context containerContext = mContext; - if (containerStyle != 0 && applyForRoot) { - containerContext = new ContextThemeWrapper(mContext, containerStyle); - } - final LinearLayout viewTreeItems = new LinearLayout(containerContext, null, containerStyle); - - viewTreeItems.setId(R.id.tree_items); - viewTreeItems.setOrientation(LinearLayout.VERTICAL); - view.addView(viewTreeItems); - - mRoot.setViewHolder(new TreeNode.BaseNodeViewHolder(mContext) { - @Override - public View createNodeView(TreeNode node, Object value) { - return null; - } - - @Override - public ViewGroup getNodeItemsView() { - return viewTreeItems; - } - }); - - expandNode(mRoot, false); - return view; - } - - public View getView() { - return getView(-1); - } - - - public void expandLevel(int level) { - for (TreeNode n : mRoot.getChildren()) { - expandLevel(n, level); - } - } - - private void expandLevel(TreeNode node, int level) { - if (node.getLevel() <= level) { - expandNode(node, false); - } - for (TreeNode n : node.getChildren()) { - expandLevel(n, level); - } - } - - public void expandNode(TreeNode node) { - expandNode(node, false); - } - - public void collapseNode(TreeNode node) { - collapseNode(node, false); - } - - public String getSaveState() { - final StringBuilder builder = new StringBuilder(); - getSaveState(mRoot, builder); - if (builder.length() > 0) { - builder.setLength(builder.length() - 1); - } - return builder.toString(); - } - - public void restoreState(String saveState) { - if (!TextUtils.isEmpty(saveState)) { - collapseAll(); - final String[] openNodesArray = saveState.split(NODES_PATH_SEPARATOR); - final Set openNodes = new HashSet<>(Arrays.asList(openNodesArray)); - restoreNodeState(mRoot, openNodes); - } - } - - private void restoreNodeState(TreeNode node, Set openNodes) { - for (TreeNode n : node.getChildren()) { - if (openNodes.contains(n.getPath())) { - expandNode(n); - restoreNodeState(n, openNodes); - } - } - } - - private void getSaveState(TreeNode root, StringBuilder sBuilder) { - for (TreeNode node : root.getChildren()) { - if (node.isExpanded()) { - sBuilder.append(node.getPath()); - sBuilder.append(NODES_PATH_SEPARATOR); - getSaveState(node, sBuilder); - } - } - } - - public void toggleNode(TreeNode node) { - if (node.isExpanded()) { - collapseNode(node, false); - } else { - expandNode(node, false); - } - - } - - private void collapseNode(TreeNode node, final boolean includeSubnodes) { - node.setExpanded(false); - TreeNode.BaseNodeViewHolder nodeViewHolder = getViewHolderForNode(node); - - if (mUseDefaultAnimation) { - collapse(nodeViewHolder.getNodeItemsView()); - } else { - nodeViewHolder.getNodeItemsView().setVisibility(View.GONE); - } - nodeViewHolder.toggle(false); - if (includeSubnodes) { - for (TreeNode n : node.getChildren()) { - collapseNode(n, includeSubnodes); - } - } - } - - private void expandNode(final TreeNode node, boolean includeSubnodes) { - node.setExpanded(true); - final TreeNode.BaseNodeViewHolder parentViewHolder = getViewHolderForNode(node); - parentViewHolder.getNodeItemsView().removeAllViews(); - - - parentViewHolder.toggle(true); - - for (final TreeNode n : node.getChildren()) { - addNode(parentViewHolder.getNodeItemsView(), n); - - if (n.isExpanded() || includeSubnodes) { - expandNode(n, includeSubnodes); - } - - } - if (mUseDefaultAnimation) { - expand(parentViewHolder.getNodeItemsView()); - } else { - parentViewHolder.getNodeItemsView().setVisibility(View.VISIBLE); - } - - } - - private void addNode(ViewGroup container, final TreeNode n) { - final TreeNode.BaseNodeViewHolder viewHolder = getViewHolderForNode(n); - final View nodeView = viewHolder.getView(); - container.addView(nodeView); - if (mSelectionModeEnabled) { - viewHolder.toggleSelectionMode(mSelectionModeEnabled); - } - - nodeView.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (n.getClickListener() != null) { - n.getClickListener().onClick(n, n.getValue()); - } else if (nodeClickListener != null) { - nodeClickListener.onClick(n, n.getValue()); - } - if (enableAutoToggle) { - toggleNode(n); - } - } - }); - - nodeView.setOnLongClickListener(new View.OnLongClickListener() { - @Override - public boolean onLongClick(View view) { - if (n.getLongClickListener() != null) { - return n.getLongClickListener().onLongClick(n, n.getValue()); - } else if (nodeLongClickListener != null) { - return nodeLongClickListener.onLongClick(n, n.getValue()); - } - if (enableAutoToggle) { - toggleNode(n); - } - return false; - } - }); - } - - //------------------------------------------------------------ - // Selection methods - - public void setSelectionModeEnabled(boolean selectionModeEnabled) { - if (!selectionModeEnabled) { - // TODO fix double iteration over tree - deselectAll(); - } - mSelectionModeEnabled = selectionModeEnabled; - - for (TreeNode node : mRoot.getChildren()) { - toggleSelectionMode(node, selectionModeEnabled); - } - - } - - public List getSelectedValues(Class clazz) { - List result = new ArrayList<>(); - List selected = getSelected(); - for (TreeNode n : selected) { - Object value = n.getValue(); - if (value != null && value.getClass().equals(clazz)) { - result.add((E) value); - } - } - return result; - } - - public boolean isSelectionModeEnabled() { - return mSelectionModeEnabled; - } - - private void toggleSelectionMode(TreeNode parent, boolean mSelectionModeEnabled) { - toogleSelectionForNode(parent, mSelectionModeEnabled); - if (parent.isExpanded()) { - for (TreeNode node : parent.getChildren()) { - toggleSelectionMode(node, mSelectionModeEnabled); - } - } - } - - public List getSelected() { - if (mSelectionModeEnabled) { - return getSelected(mRoot); - } else { - return new ArrayList<>(); - } - } - - // TODO Do we need to go through whole tree? Save references or consider collapsed nodes as not selected - private List getSelected(TreeNode parent) { - List result = new ArrayList<>(); - for (TreeNode n : parent.getChildren()) { - if (n.isSelected()) { - result.add(n); - } - result.addAll(getSelected(n)); - } - return result; - } - - public void selectAll(boolean skipCollapsed) { - makeAllSelection(true, skipCollapsed); - } - - public void deselectAll() { - makeAllSelection(false, false); - } - - private void makeAllSelection(boolean selected, boolean skipCollapsed) { - if (mSelectionModeEnabled) { - for (TreeNode node : mRoot.getChildren()) { - selectNode(node, selected, skipCollapsed); - } - } - } - - public void selectNode(TreeNode node, boolean selected) { - if (mSelectionModeEnabled) { - node.setSelected(selected); - toogleSelectionForNode(node, true); - } - } - - private void selectNode(TreeNode parent, boolean selected, boolean skipCollapsed) { - parent.setSelected(selected); - toogleSelectionForNode(parent, true); - boolean toContinue = skipCollapsed ? parent.isExpanded() : true; - if (toContinue) { - for (TreeNode node : parent.getChildren()) { - selectNode(node, selected, skipCollapsed); - } - } - } - - private void toogleSelectionForNode(TreeNode node, boolean makeSelectable) { - TreeNode.BaseNodeViewHolder holder = getViewHolderForNode(node); - if (holder.isInitialized()) { - getViewHolderForNode(node).toggleSelectionMode(makeSelectable); - } - } - - private TreeNode.BaseNodeViewHolder getViewHolderForNode(TreeNode node) { - TreeNode.BaseNodeViewHolder viewHolder = node.getViewHolder(); - if (viewHolder == null) { - try { - final Object object = defaultViewHolderClass.getConstructor(Context.class).newInstance(mContext); - viewHolder = (TreeNode.BaseNodeViewHolder) object; - node.setViewHolder(viewHolder); - } catch (Exception e) { - throw new RuntimeException("Could not instantiate class " + defaultViewHolderClass); - } - } - if (viewHolder.getContainerStyle() <= 0) { - viewHolder.setContainerStyle(containerStyle); - } - if (viewHolder.getTreeView() == null) { - viewHolder.setTreeViev(this); - } - return viewHolder; - } - - private static void expand(final View v) { - v.measure(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); - final int targetHeight = v.getMeasuredHeight(); - - v.getLayoutParams().height = 0; - v.setVisibility(View.VISIBLE); - Animation a = new Animation() { - @Override - protected void applyTransformation(float interpolatedTime, Transformation t) { - v.getLayoutParams().height = interpolatedTime == 1 - ? LinearLayout.LayoutParams.WRAP_CONTENT - : (int) (targetHeight * interpolatedTime); - v.requestLayout(); - } - - @Override - public boolean willChangeBounds() { - return true; - } - }; - - // 1dp/ms - a.setDuration((int) (targetHeight / v.getContext().getResources().getDisplayMetrics().density)); - v.startAnimation(a); - } - - private static void collapse(final View v) { - final int initialHeight = v.getMeasuredHeight(); - - Animation a = new Animation() { - @Override - protected void applyTransformation(float interpolatedTime, Transformation t) { - if (interpolatedTime == 1) { - v.setVisibility(View.GONE); - } else { - v.getLayoutParams().height = initialHeight - (int) (initialHeight * interpolatedTime); - v.requestLayout(); - } - } - - @Override - public boolean willChangeBounds() { - return true; - } - }; - - // 1dp/ms - a.setDuration((int) (initialHeight / v.getContext().getResources().getDisplayMetrics().density)); - v.startAnimation(a); - } - - //----------------------------------------------------------------- - //Add / Remove - - public void addNode(TreeNode parent, final TreeNode nodeToAdd) { - parent.addChild(nodeToAdd); - if (parent.isExpanded()) { - final TreeNode.BaseNodeViewHolder parentViewHolder = getViewHolderForNode(parent); - addNode(parentViewHolder.getNodeItemsView(), nodeToAdd); - } - } - - public void removeNode(TreeNode node) { - if (node.getParent() != null) { - TreeNode parent = node.getParent(); - int index = parent.deleteChild(node); - if (parent.isExpanded() && index >= 0) { - final TreeNode.BaseNodeViewHolder parentViewHolder = getViewHolderForNode(parent); - parentViewHolder.getNodeItemsView().removeViewAt(index); - } - } - } -} diff --git a/library/src/main/java/com/unnamed/b/atv/view/AndroidTreeView.kt b/library/src/main/java/com/unnamed/b/atv/view/AndroidTreeView.kt new file mode 100644 index 0000000..092ca6d --- /dev/null +++ b/library/src/main/java/com/unnamed/b/atv/view/AndroidTreeView.kt @@ -0,0 +1,480 @@ +package com.unnamed.b.atv.view + +import android.content.Context +import android.text.TextUtils +import android.view.ContextThemeWrapper +import android.view.View +import android.view.ViewGroup +import android.view.animation.Animation +import android.view.animation.Transformation +import android.widget.LinearLayout +import android.widget.ScrollView +import com.unnamed.b.atv.R +import com.unnamed.b.atv.holder.SimpleViewHolder +import com.unnamed.b.atv.model.TreeNode +import java.util.* + +/** + * Created by Bogdan Melnychuk on 2/10/15. + * Converted to Kolin by Kumar Shivang on 16/07/17 + */ +class AndroidTreeView { + + protected var mRoot: TreeNode? = null + private var mContext: Context? = null + private var applyForRoot: Boolean = false + private var containerStyle = 0 + private var defaultViewHolderClass: Class> = SimpleViewHolder::class.java + private var nodeClickListener: TreeNode.TreeNodeClickListener? = null + private var nodeLongClickListener: TreeNode.TreeNodeLongClickListener? = null + //------------------------------------------------------------ + // Selection methods + + // TODO fix double iteration over tree + var isSelectionModeEnabled: Boolean = false + set(selectionModeEnabled) { + if (!selectionModeEnabled) { + deselectAll() + } + field = selectionModeEnabled + + if (mRoot != null) { + for (node in mRoot!!.getChildren()) { + toggleSelectionMode(node, selectionModeEnabled) + } + } + + } + private var mUseDefaultAnimation = false + var is2dScrollEnabled = false + private set + var isAutoToggleEnabled = true + private set + + constructor(context: Context) { + mContext = context + } + + fun setRoot(mRoot: TreeNode) { + this.mRoot = mRoot + } + + constructor(context: Context, root: TreeNode) { + mRoot = root + mContext = context + } + + fun setDefaultAnimation(defaultAnimation: Boolean) { + this.mUseDefaultAnimation = defaultAnimation + } + + @JvmOverloads fun setDefaultContainerStyle(style: Int, applyForRoot: Boolean = false) { + containerStyle = style + this.applyForRoot = applyForRoot + } + + fun setUse2dScroll(use2dScroll: Boolean) { + this.is2dScrollEnabled = use2dScroll + } + + fun setUseAutoToggle(enableAutoToggle: Boolean) { + this.isAutoToggleEnabled = enableAutoToggle + } + + fun setDefaultViewHolder(viewHolder: Class>) { + defaultViewHolderClass = viewHolder + } + + fun setDefaultNodeClickListener(listener: TreeNode.TreeNodeClickListener) { + nodeClickListener = listener + } + + fun setDefaultNodeLongClickListener(listener: TreeNode.TreeNodeLongClickListener) { + nodeLongClickListener = listener + } + + fun expandAll() { + if (mRoot != null) { + expandNode(mRoot!!, true) + } + } + + fun collapseAll() { + if (mRoot != null) { + for (n in mRoot!!.getChildren()) { + collapseNode(n, true) + } + } + } + + + fun getView(style: Int): View { + val view: ViewGroup + if (style > 0) { + val newContext = ContextThemeWrapper(mContext, style) + view = if (is2dScrollEnabled) TwoDScrollView(newContext) else ScrollView(newContext) + } else { + view = if (is2dScrollEnabled) TwoDScrollView(mContext!!) else ScrollView(mContext) + } + + var containerContext: Context? = mContext + if (containerStyle != 0 && applyForRoot) { + containerContext = ContextThemeWrapper(mContext, containerStyle) + } + val viewTreeItems = LinearLayout(containerContext, null, containerStyle) + + viewTreeItems.id = R.id.tree_items + viewTreeItems.orientation = LinearLayout.VERTICAL + view.addView(viewTreeItems) + + if (mRoot != null) { + mRoot!!.setViewHolder(object : TreeNode.BaseNodeViewHolder(mContext!!) { + override fun createNodeView(node: TreeNode, value: Any): View? { + return null + } + + override val nodeItemsView: ViewGroup + get() = viewTreeItems + }) + expandNode(mRoot!!, false) + } + return view + } + + val view: View + get() = getView(-1) + + + fun expandLevel(level: Int) { + if (mRoot != null) { + for (n in mRoot!!.getChildren()) { + expandLevel(n, level) + } + } + } + + private fun expandLevel(node: TreeNode, level: Int) { + if (node.level <= level) { + expandNode(node, false) + } + for (n in node.getChildren()) { + expandLevel(n, level) + } + } + + fun expandNode(node: TreeNode) { + expandNode(node, false) + } + + fun collapseNode(node: TreeNode) { + collapseNode(node, false) + } + + val saveState: String + get() { + val builder = StringBuilder() + if (mRoot != null) { + getSaveState(mRoot!!, builder) + } + if (builder.isNotEmpty()) { + builder.setLength(builder.length - 1) + } + return builder.toString() + } + + fun restoreState(saveState: String) { + if (!TextUtils.isEmpty(saveState)) { + collapseAll() + val openNodesArray = saveState.split(NODES_PATH_SEPARATOR.toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() + val openNodes = HashSet(Arrays.asList(*openNodesArray)) + if (mRoot != null) { + restoreNodeState(mRoot!!, openNodes) + } + } + } + + private fun restoreNodeState(node: TreeNode, openNodes: Set) { + for (n in node.getChildren()) { + if (openNodes.contains(n.path)) { + expandNode(n) + restoreNodeState(n, openNodes) + } + } + } + + private fun getSaveState(root: TreeNode, sBuilder: StringBuilder) { + for (node in root.getChildren()) { + if (node.isExpanded()) { + sBuilder.append(node.path) + sBuilder.append(NODES_PATH_SEPARATOR) + getSaveState(node, sBuilder) + } + } + } + + fun toggleNode(node: TreeNode) { + if (node.isExpanded()) { + collapseNode(node, false) + } else { + expandNode(node, false) + } + + } + + private fun collapseNode(node: TreeNode, includeSubnodes: Boolean) { + node.setExpanded(false) + val nodeViewHolder = getViewHolderForNode(node) + + if (mUseDefaultAnimation) { + val view = nodeViewHolder?.nodeItemsView + if (view != null) { + collapse(view) + } + } else { + nodeViewHolder?.nodeItemsView?.visibility = View.GONE + } + nodeViewHolder?.toggle(false) + if (includeSubnodes) { + for (n in node.getChildren()) { + collapseNode(n, includeSubnodes) + } + } + } + + private fun expandNode(node: TreeNode, includeSubnodes: Boolean) { + node.setExpanded(true) + val parentViewHolder = getViewHolderForNode(node) + parentViewHolder?.nodeItemsView?.removeAllViews() + + + parentViewHolder?.toggle(true) + + for (n in node.getChildren()) { + addNode(parentViewHolder?.nodeItemsView, n) + + if (n.isExpanded() || includeSubnodes) { + expandNode(n, includeSubnodes) + } + + } + if (mUseDefaultAnimation) { + val parentView = parentViewHolder?.nodeItemsView + if (parentView != null) { + expand(parentView) + } + } else { + parentViewHolder?.nodeItemsView?.visibility = View.VISIBLE + } + + } + + private fun addNode(container: ViewGroup?, n: TreeNode) { + val viewHolder = getViewHolderForNode(n) + val nodeView = viewHolder?.view + container?.addView(nodeView) + if (isSelectionModeEnabled) { + viewHolder?.toggleSelectionMode(isSelectionModeEnabled) + } + + nodeView?.setOnClickListener { + if (n.getClickListener() != null) { + n.getClickListener()!!.onClick(n, n.value!!) + } else if (nodeClickListener != null) { + nodeClickListener!!.onClick(n, n.value!!) + } + if (isAutoToggleEnabled) { + toggleNode(n) + } + } + + nodeView?.setOnLongClickListener(View.OnLongClickListener { + if (n.getLongClickListener() != null) { + return@OnLongClickListener n.getLongClickListener()!!.onLongClick(n, n.value!!) + } else if (nodeLongClickListener != null) { + return@OnLongClickListener nodeLongClickListener!!.onLongClick(n, n.value!!) + } + if (isAutoToggleEnabled) { + toggleNode(n) + } + false + }) + } + + fun getSelectedValues(clazz: Class): List { + val selected = selected + val result = selected + .map { it.value } + .filter { it != null && it.javaClass == clazz && it as? E != null } + .map { it as E } + return result + } + + private fun toggleSelectionMode(parent: TreeNode, mSelectionModeEnabled: Boolean) { + toogleSelectionForNode(parent, mSelectionModeEnabled) + if (parent.isExpanded()) { + for (node in parent.getChildren()) { + toggleSelectionMode(node, mSelectionModeEnabled) + } + } + } + + val selected: List + get() { + if (isSelectionModeEnabled) { + return getSelected(mRoot!!) + } else { + return ArrayList() + } + } + + // TODO Do we need to go through whole tree? Save references or consider collapsed nodes as not selected + private fun getSelected(parent: TreeNode): List { + val result = ArrayList() + for (n in parent.getChildren()) { + if (n.isSelected) { + result.add(n) + } + result.addAll(getSelected(n)) + } + return result + } + + fun selectAll(skipCollapsed: Boolean) { + makeAllSelection(true, skipCollapsed) + } + + fun deselectAll() { + makeAllSelection(false, false) + } + + private fun makeAllSelection(selected: Boolean, skipCollapsed: Boolean) { + if (isSelectionModeEnabled && mRoot != null) { + for (node in mRoot!!.getChildren()) { + selectNode(node, selected, skipCollapsed) + } + } + } + + fun selectNode(node: TreeNode, selected: Boolean) { + if (isSelectionModeEnabled) { + node.isSelected = selected + toogleSelectionForNode(node, true) + } + } + + private fun selectNode(parent: TreeNode, selected: Boolean, skipCollapsed: Boolean) { + parent.isSelected = selected + toogleSelectionForNode(parent, true) + val toContinue = if (skipCollapsed) parent.isExpanded() else true + if (toContinue) { + for (node in parent.getChildren()) { + selectNode(node, selected, skipCollapsed) + } + } + } + + private fun toogleSelectionForNode(node: TreeNode, makeSelectable: Boolean) { + val holder = getViewHolderForNode(node) + if (holder?.isInitialized == true) { + getViewHolderForNode(node)?.toggleSelectionMode(makeSelectable) + } + } + + private fun getViewHolderForNode(node: TreeNode): TreeNode.BaseNodeViewHolder<*>? { + var viewHolder: TreeNode.BaseNodeViewHolder<*>? = node.getViewHolder() + if (viewHolder == null) { + try { + val `object` = defaultViewHolderClass.getConstructor(Context::class.java).newInstance(mContext) + viewHolder = `object` + node.setViewHolder(viewHolder) + } catch (e: Exception) { + throw RuntimeException("Could not instantiate class " + defaultViewHolderClass) + } + + } + if (viewHolder?.treeView == null) { + viewHolder?.setTreeViev(this) + } + if (viewHolder != null && viewHolder.containerStyle <= 0) { + viewHolder.containerStyle = containerStyle + } + return viewHolder + } + + //----------------------------------------------------------------- + //Add / Remove + + fun addNode(parent: TreeNode, nodeToAdd: TreeNode) { + parent.addChild(nodeToAdd) + if (parent.isExpanded()) { + val parentViewHolder = getViewHolderForNode(parent) + addNode(parentViewHolder?.nodeItemsView, nodeToAdd) + } + } + + fun removeNode(node: TreeNode) { + if (node.parent != null) { + val parent = node.parent + val index = parent!!.deleteChild(node) + if (parent.isExpanded() && index >= 0) { + val parentViewHolder = getViewHolderForNode(parent) + parentViewHolder?.nodeItemsView?.removeViewAt(index) + } + } + } + + companion object { + private val NODES_PATH_SEPARATOR = ";" + + private fun expand(v: View) { + v.measure(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT) + val targetHeight = v.measuredHeight + + v.layoutParams.height = 0 + v.visibility = View.VISIBLE + val a = object : Animation() { + override fun applyTransformation(interpolatedTime: Float, t: Transformation) { + v.layoutParams.height = if (interpolatedTime == 1f) + LinearLayout.LayoutParams.WRAP_CONTENT + else + (targetHeight * interpolatedTime).toInt() + v.requestLayout() + } + + override fun willChangeBounds(): Boolean { + return true + } + } + + // 1dp/ms + a.duration = (targetHeight / v.context.resources.displayMetrics.density).toInt().toLong() + v.startAnimation(a) + } + + private fun collapse(v: View) { + val initialHeight = v.measuredHeight + + val a = object : Animation() { + override fun applyTransformation(interpolatedTime: Float, t: Transformation) { + if (interpolatedTime == 1f) { + v.visibility = View.GONE + } else { + + v.layoutParams?.height = initialHeight - (initialHeight * interpolatedTime).toInt() + v.requestLayout() + } + } + + override fun willChangeBounds(): Boolean { + return true + } + } + + // 1dp/ms + val density = v.context?.resources?.displayMetrics?.density + if (density != null) + a.duration = (initialHeight / density).toInt().toLong() + v.startAnimation(a) + } + } +} diff --git a/library/src/main/java/com/unnamed/b/atv/view/TreeNodeWrapperView.java b/library/src/main/java/com/unnamed/b/atv/view/TreeNodeWrapperView.java deleted file mode 100644 index b660563..0000000 --- a/library/src/main/java/com/unnamed/b/atv/view/TreeNodeWrapperView.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.unnamed.b.atv.view; - -import android.content.Context; -import android.view.ContextThemeWrapper; -import android.view.View; -import android.view.ViewGroup; -import android.widget.LinearLayout; -import android.widget.RelativeLayout; - -import com.unnamed.b.atv.R; - -/** - * Created by Bogdan Melnychuk on 2/10/15. - */ -public class TreeNodeWrapperView extends LinearLayout { - private LinearLayout nodeItemsContainer; - private ViewGroup nodeContainer; - private final int containerStyle; - - public TreeNodeWrapperView(Context context, int containerStyle) { - super(context); - this.containerStyle = containerStyle; - init(); - } - - private void init() { - setOrientation(LinearLayout.VERTICAL); - - nodeContainer = new RelativeLayout(getContext()); - nodeContainer.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); - nodeContainer.setId(R.id.node_header); - - ContextThemeWrapper newContext = new ContextThemeWrapper(getContext(), containerStyle); - nodeItemsContainer = new LinearLayout(newContext, null, containerStyle); - nodeItemsContainer.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); - nodeItemsContainer.setId(R.id.node_items); - nodeItemsContainer.setOrientation(LinearLayout.VERTICAL); - nodeItemsContainer.setVisibility(View.GONE); - - addView(nodeContainer); - addView(nodeItemsContainer); - } - - - public void insertNodeView(View nodeView) { - nodeContainer.addView(nodeView); - } - - public ViewGroup getNodeContainer() { - return nodeContainer; - } -} diff --git a/library/src/main/java/com/unnamed/b/atv/view/TreeNodeWrapperView.kt b/library/src/main/java/com/unnamed/b/atv/view/TreeNodeWrapperView.kt new file mode 100644 index 0000000..4beecbe --- /dev/null +++ b/library/src/main/java/com/unnamed/b/atv/view/TreeNodeWrapperView.kt @@ -0,0 +1,47 @@ +package com.unnamed.b.atv.view + +import android.content.Context +import android.view.ContextThemeWrapper +import android.view.View +import android.view.ViewGroup +import android.widget.LinearLayout +import android.widget.RelativeLayout + +import com.unnamed.b.atv.R + +/** + * Created by Bogdan Melnychuk on 2/10/15. + * Converted to Kolin by Kumar Shivang on 16/07/17 + */ +class TreeNodeWrapperView(context: Context, private val containerStyle: Int) : LinearLayout(context) { + private var nodeItemsContainer: LinearLayout? = null + var nodeContainer: ViewGroup? = null + private set + + init { + init() + } + + private fun init() { + orientation = LinearLayout.VERTICAL + + nodeContainer = RelativeLayout(context) + nodeContainer!!.layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT) + nodeContainer!!.id = R.id.node_header + + val newContext = ContextThemeWrapper(context, containerStyle) + nodeItemsContainer = LinearLayout(newContext, null, containerStyle) + nodeItemsContainer!!.layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT) + nodeItemsContainer!!.id = R.id.node_items + nodeItemsContainer!!.orientation = LinearLayout.VERTICAL + nodeItemsContainer!!.visibility = View.GONE + + addView(nodeContainer) + addView(nodeItemsContainer) + } + + + fun insertNodeView(nodeView: View) { + nodeContainer!!.addView(nodeView) + } +} diff --git a/library/src/main/java/com/unnamed/b/atv/view/TwoDScrollView.java b/library/src/main/java/com/unnamed/b/atv/view/TwoDScrollView.java deleted file mode 100644 index 298e060..0000000 --- a/library/src/main/java/com/unnamed/b/atv/view/TwoDScrollView.java +++ /dev/null @@ -1,1110 +0,0 @@ -package com.unnamed.b.atv.view; - - -import android.content.Context; -import android.graphics.Rect; -import android.util.AttributeSet; -import android.view.FocusFinder; -import android.view.KeyEvent; -import android.view.MotionEvent; -import android.view.VelocityTracker; -import android.view.View; -import android.view.ViewConfiguration; -import android.view.ViewGroup; -import android.view.ViewParent; -import android.view.animation.AnimationUtils; -import android.widget.FrameLayout; -import android.widget.LinearLayout; -import android.widget.Scroller; -import android.widget.TextView; - -import java.util.List; - -/** - * Layout container for a view hierarchy that can be scrolled by the user, - * allowing it to be larger than the physical display. A TwoDScrollView - * is a {@link FrameLayout}, meaning you should place one child in it - * containing the entire contents to scroll; this child may itself be a layout - * manager with a complex hierarchy of objects. A child that is often used - * is a {@link LinearLayout} in a vertical orientation, presenting a vertical - * array of top-level items that the user can scroll through. - *

- *

The {@link TextView} class also - * takes care of its own scrolling, so does not require a TwoDScrollView, but - * using the two together is possible to achieve the effect of a text view - * within a larger container. - */ -public class TwoDScrollView extends FrameLayout { - static final int ANIMATED_SCROLL_GAP = 250; - static final float MAX_SCROLL_FACTOR = 0.5f; - - private long mLastScroll; - - private final Rect mTempRect = new Rect(); - private Scroller mScroller; - - /** - * Flag to indicate that we are moving focus ourselves. This is so the - * code that watches for focus changes initiated outside this TwoDScrollView - * knows that it does not have to do anything. - */ - private boolean mTwoDScrollViewMovedFocus; - - /** - * Position of the last motion event. - */ - private float mLastMotionY; - private float mLastMotionX; - - /** - * True when the layout has changed but the traversal has not come through yet. - * Ideally the view hierarchy would keep track of this for us. - */ - private boolean mIsLayoutDirty = true; - - /** - * The child to give focus to in the event that a child has requested focus while the - * layout is dirty. This prevents the scroll from being wrong if the child has not been - * laid out before requesting focus. - */ - private View mChildToScrollTo = null; - - /** - * True if the user is currently dragging this TwoDScrollView around. This is - * not the same as 'is being flinged', which can be checked by - * mScroller.isFinished() (flinging begins when the user lifts his finger). - */ - private boolean mIsBeingDragged = false; - - /** - * Determines speed during touch scrolling - */ - private VelocityTracker mVelocityTracker; - - /** - * Whether arrow scrolling is animated. - */ - private int mTouchSlop; - private int mMinimumVelocity; - private int mMaximumVelocity; - - public TwoDScrollView(Context context) { - super(context); - initTwoDScrollView(); - } - - public TwoDScrollView(Context context, AttributeSet attrs) { - super(context, attrs); - initTwoDScrollView(); - } - - public TwoDScrollView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - initTwoDScrollView(); - } - - @Override - protected float getTopFadingEdgeStrength() { - if (getChildCount() == 0) { - return 0.0f; - } - final int length = getVerticalFadingEdgeLength(); - if (getScrollY() < length) { - return getScrollY() / (float) length; - } - return 1.0f; - } - - @Override - protected float getBottomFadingEdgeStrength() { - if (getChildCount() == 0) { - return 0.0f; - } - final int length = getVerticalFadingEdgeLength(); - final int bottomEdge = getHeight() - getPaddingBottom(); - final int span = getChildAt(0).getBottom() - getScrollY() - bottomEdge; - if (span < length) { - return span / (float) length; - } - return 1.0f; - } - - @Override - protected float getLeftFadingEdgeStrength() { - if (getChildCount() == 0) { - return 0.0f; - } - final int length = getHorizontalFadingEdgeLength(); - if (getScrollX() < length) { - return getScrollX() / (float) length; - } - return 1.0f; - } - - @Override - protected float getRightFadingEdgeStrength() { - if (getChildCount() == 0) { - return 0.0f; - } - final int length = getHorizontalFadingEdgeLength(); - final int rightEdge = getWidth() - getPaddingRight(); - final int span = getChildAt(0).getRight() - getScrollX() - rightEdge; - if (span < length) { - return span / (float) length; - } - return 1.0f; - } - - /** - * @return The maximum amount this scroll view will scroll in response to - * an arrow event. - */ - public int getMaxScrollAmountVertical() { - return (int) (MAX_SCROLL_FACTOR * getHeight()); - } - - public int getMaxScrollAmountHorizontal() { - return (int) (MAX_SCROLL_FACTOR * getWidth()); - } - - private void initTwoDScrollView() { - mScroller = new Scroller(getContext()); - setFocusable(true); - setDescendantFocusability(FOCUS_AFTER_DESCENDANTS); - setWillNotDraw(false); - final ViewConfiguration configuration = ViewConfiguration.get(getContext()); - mTouchSlop = configuration.getScaledTouchSlop(); - mMinimumVelocity = configuration.getScaledMinimumFlingVelocity(); - mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); - } - - @Override - public void addView(View child) { - if (getChildCount() > 0) { - throw new IllegalStateException("TwoDScrollView can host only one direct child"); - } - super.addView(child); - } - - @Override - public void addView(View child, int index) { - if (getChildCount() > 0) { - throw new IllegalStateException("TwoDScrollView can host only one direct child"); - } - super.addView(child, index); - } - - @Override - public void addView(View child, ViewGroup.LayoutParams params) { - if (getChildCount() > 0) { - throw new IllegalStateException("TwoDScrollView can host only one direct child"); - } - super.addView(child, params); - } - - @Override - public void addView(View child, int index, ViewGroup.LayoutParams params) { - if (getChildCount() > 0) { - throw new IllegalStateException("TwoDScrollView can host only one direct child"); - } - super.addView(child, index, params); - } - - /** - * @return Returns true this TwoDScrollView can be scrolled - */ - private boolean canScroll() { - View child = getChildAt(0); - if (child != null) { - int childHeight = child.getHeight(); - int childWidth = child.getWidth(); - return (getHeight() < childHeight + getPaddingTop() + getPaddingBottom()) || - (getWidth() < childWidth + getPaddingLeft() + getPaddingRight()); - } - return false; - } - - @Override - public boolean dispatchKeyEvent(KeyEvent event) { - // Let the focused view and/or our descendants get the key first - boolean handled = super.dispatchKeyEvent(event); - if (handled) { - return true; - } - return executeKeyEvent(event); - } - - /** - * You can call this function yourself to have the scroll view perform - * scrolling from a key event, just as if the event had been dispatched to - * it by the view hierarchy. - * - * @param event The key event to execute. - * @return Return true if the event was handled, else false. - */ - public boolean executeKeyEvent(KeyEvent event) { - mTempRect.setEmpty(); - if (!canScroll()) { - if (isFocused()) { - View currentFocused = findFocus(); - if (currentFocused == this) currentFocused = null; - View nextFocused = FocusFinder.getInstance().findNextFocus(this, currentFocused, View.FOCUS_DOWN); - return nextFocused != null && nextFocused != this && nextFocused.requestFocus(View.FOCUS_DOWN); - } - return false; - } - boolean handled = false; - if (event.getAction() == KeyEvent.ACTION_DOWN) { - switch (event.getKeyCode()) { - case KeyEvent.KEYCODE_DPAD_UP: - if (!event.isAltPressed()) { - handled = arrowScroll(View.FOCUS_UP, false); - } else { - handled = fullScroll(View.FOCUS_UP, false); - } - break; - case KeyEvent.KEYCODE_DPAD_DOWN: - if (!event.isAltPressed()) { - handled = arrowScroll(View.FOCUS_DOWN, false); - } else { - handled = fullScroll(View.FOCUS_DOWN, false); - } - break; - case KeyEvent.KEYCODE_DPAD_LEFT: - if (!event.isAltPressed()) { - handled = arrowScroll(View.FOCUS_LEFT, true); - } else { - handled = fullScroll(View.FOCUS_LEFT, true); - } - break; - case KeyEvent.KEYCODE_DPAD_RIGHT: - if (!event.isAltPressed()) { - handled = arrowScroll(View.FOCUS_RIGHT, true); - } else { - handled = fullScroll(View.FOCUS_RIGHT, true); - } - break; - } - } - return handled; - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - /* - * This method JUST determines whether we want to intercept the motion. - * If we return true, onMotionEvent will be called and we do the actual - * scrolling there. - * - * Shortcut the most recurring case: the user is in the dragging - * state and he is moving his finger. We want to intercept this - * motion. - */ - final int action = ev.getAction(); - if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) { - return true; - } - if (!canScroll()) { - mIsBeingDragged = false; - return false; - } - final float y = ev.getY(); - final float x = ev.getX(); - switch (action) { - case MotionEvent.ACTION_MOVE: - /* - * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check - * whether the user has moved far enough from his original down touch. - */ - /* - * Locally do absolute value. mLastMotionY is set to the y value - * of the down event. - */ - final int yDiff = (int) Math.abs(y - mLastMotionY); - final int xDiff = (int) Math.abs(x - mLastMotionX); - if (yDiff > mTouchSlop || xDiff > mTouchSlop) { - mIsBeingDragged = true; - } - break; - - case MotionEvent.ACTION_DOWN: - /* Remember location of down touch */ - mLastMotionY = y; - mLastMotionX = x; - - /* - * If being flinged and user touches the screen, initiate drag; - * otherwise don't. mScroller.isFinished should be false when - * being flinged. - */ - mIsBeingDragged = !mScroller.isFinished(); - break; - - case MotionEvent.ACTION_CANCEL: - case MotionEvent.ACTION_UP: - /* Release the drag */ - mIsBeingDragged = false; - break; - } - - /* - * The only time we want to intercept motion events is if we are in the - * drag mode. - */ - return mIsBeingDragged; - } - - @Override - public boolean onTouchEvent(MotionEvent ev) { - - if (ev.getAction() == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() != 0) { - // Don't handle edge touches immediately -- they may actually belong to one of our - // descendants. - return false; - } - - if (!canScroll()) { - return false; - } - - if (mVelocityTracker == null) { - mVelocityTracker = VelocityTracker.obtain(); - } - mVelocityTracker.addMovement(ev); - - final int action = ev.getAction(); - final float y = ev.getY(); - final float x = ev.getX(); - - switch (action) { - case MotionEvent.ACTION_DOWN: - /* - * If being flinged and user touches, stop the fling. isFinished - * will be false if being flinged. - */ - if (!mScroller.isFinished()) { - mScroller.abortAnimation(); - } - - // Remember where the motion event started - mLastMotionY = y; - mLastMotionX = x; - break; - case MotionEvent.ACTION_MOVE: - // Scroll to follow the motion event - int deltaX = (int) (mLastMotionX - x); - int deltaY = (int) (mLastMotionY - y); - mLastMotionX = x; - mLastMotionY = y; - - if (deltaX < 0) { - if (getScrollX() < 0) { - deltaX = 0; - } - } else if (deltaX > 0) { - final int rightEdge = getWidth() - getPaddingRight(); - final int availableToScroll = getChildAt(0).getRight() - getScrollX() - rightEdge; - if (availableToScroll > 0) { - deltaX = Math.min(availableToScroll, deltaX); - } else { - deltaX = 0; - } - } - if (deltaY < 0) { - if (getScrollY() < 0) { - deltaY = 0; - } - } else if (deltaY > 0) { - final int bottomEdge = getHeight() - getPaddingBottom(); - final int availableToScroll = getChildAt(0).getBottom() - getScrollY() - bottomEdge; - if (availableToScroll > 0) { - deltaY = Math.min(availableToScroll, deltaY); - } else { - deltaY = 0; - } - } - if (deltaY != 0 || deltaX != 0) - scrollBy(deltaX, deltaY); - break; - case MotionEvent.ACTION_UP: - final VelocityTracker velocityTracker = mVelocityTracker; - velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); - int initialXVelocity = (int) velocityTracker.getXVelocity(); - int initialYVelocity = (int) velocityTracker.getYVelocity(); - if ((Math.abs(initialXVelocity) + Math.abs(initialYVelocity) > mMinimumVelocity) && getChildCount() > 0) { - fling(-initialXVelocity, -initialYVelocity); - } - if (mVelocityTracker != null) { - mVelocityTracker.recycle(); - mVelocityTracker = null; - } - } - return true; - } - - /** - * Finds the next focusable component that fits in this View's bounds - * (excluding fading edges) pretending that this View's top is located at - * the parameter top. - * - * @param topFocus look for a candidate is the one at the top of the bounds - * if topFocus is true, or at the bottom of the bounds if topFocus is - * false - * @param top the top offset of the bounds in which a focusable must be - * found (the fading edge is assumed to start at this position) - * @param preferredFocusable the View that has highest priority and will be - * returned if it is within my bounds (null is valid) - * @return the next focusable component in the bounds or null if none can be - * found - */ - private View findFocusableViewInMyBounds(final boolean topFocus, final int top, final boolean leftFocus, final int left, View preferredFocusable) { - /* - * The fading edge's transparent side should be considered for focus - * since it's mostly visible, so we divide the actual fading edge length - * by 2. - */ - final int verticalFadingEdgeLength = getVerticalFadingEdgeLength() / 2; - final int topWithoutFadingEdge = top + verticalFadingEdgeLength; - final int bottomWithoutFadingEdge = top + getHeight() - verticalFadingEdgeLength; - final int horizontalFadingEdgeLength = getHorizontalFadingEdgeLength() / 2; - final int leftWithoutFadingEdge = left + horizontalFadingEdgeLength; - final int rightWithoutFadingEdge = left + getWidth() - horizontalFadingEdgeLength; - - if ((preferredFocusable != null) - && (preferredFocusable.getTop() < bottomWithoutFadingEdge) - && (preferredFocusable.getBottom() > topWithoutFadingEdge) - && (preferredFocusable.getLeft() < rightWithoutFadingEdge) - && (preferredFocusable.getRight() > leftWithoutFadingEdge)) { - return preferredFocusable; - } - return findFocusableViewInBounds(topFocus, topWithoutFadingEdge, bottomWithoutFadingEdge, leftFocus, leftWithoutFadingEdge, rightWithoutFadingEdge); - } - - /** - * Finds the next focusable component that fits in the specified bounds. - *

- * - * @param topFocus look for a candidate is the one at the top of the bounds - * if topFocus is true, or at the bottom of the bounds if topFocus is - * false - * @param top the top offset of the bounds in which a focusable must be - * found - * @param bottom the bottom offset of the bounds in which a focusable must - * be found - * @return the next focusable component in the bounds or null if none can - * be found - */ - private View findFocusableViewInBounds(boolean topFocus, int top, int bottom, boolean leftFocus, int left, int right) { - List focusables = getFocusables(View.FOCUS_FORWARD); - View focusCandidate = null; - - /* - * A fully contained focusable is one where its top is below the bound's - * top, and its bottom is above the bound's bottom. A partially - * contained focusable is one where some part of it is within the - * bounds, but it also has some part that is not within bounds. A fully contained - * focusable is preferred to a partially contained focusable. - */ - boolean foundFullyContainedFocusable = false; - - int count = focusables.size(); - for (int i = 0; i < count; i++) { - View view = focusables.get(i); - int viewTop = view.getTop(); - int viewBottom = view.getBottom(); - int viewLeft = view.getLeft(); - int viewRight = view.getRight(); - - if (top < viewBottom && viewTop < bottom && left < viewRight && viewLeft < right) { - /* - * the focusable is in the target area, it is a candidate for - * focusing - */ - final boolean viewIsFullyContained = (top < viewTop) && (viewBottom < bottom) && (left < viewLeft) && (viewRight < right); - if (focusCandidate == null) { - /* No candidate, take this one */ - focusCandidate = view; - foundFullyContainedFocusable = viewIsFullyContained; - } else { - final boolean viewIsCloserToVerticalBoundary = - (topFocus && viewTop < focusCandidate.getTop()) || - (!topFocus && viewBottom > focusCandidate.getBottom()); - final boolean viewIsCloserToHorizontalBoundary = - (leftFocus && viewLeft < focusCandidate.getLeft()) || - (!leftFocus && viewRight > focusCandidate.getRight()); - if (foundFullyContainedFocusable) { - if (viewIsFullyContained && viewIsCloserToVerticalBoundary && viewIsCloserToHorizontalBoundary) { - /* - * We're dealing with only fully contained views, so - * it has to be closer to the boundary to beat our - * candidate - */ - focusCandidate = view; - } - } else { - if (viewIsFullyContained) { - /* Any fully contained view beats a partially contained view */ - focusCandidate = view; - foundFullyContainedFocusable = true; - } else if (viewIsCloserToVerticalBoundary && viewIsCloserToHorizontalBoundary) { - /* - * Partially contained view beats another partially - * contained view if it's closer - */ - focusCandidate = view; - } - } - } - } - } - return focusCandidate; - } - - /** - *

Handles scrolling in response to a "home/end" shortcut press. This - * method will scroll the view to the top or bottom and give the focus - * to the topmost/bottommost component in the new visible area. If no - * component is a good candidate for focus, this scrollview reclaims the - * focus.

- * - * @param direction the scroll direction: {@link android.view.View#FOCUS_UP} - * to go the top of the view or - * {@link android.view.View#FOCUS_DOWN} to go the bottom - * @return true if the key event is consumed by this method, false otherwise - */ - public boolean fullScroll(int direction, boolean horizontal) { - if (!horizontal) { - boolean down = direction == View.FOCUS_DOWN; - int height = getHeight(); - mTempRect.top = 0; - mTempRect.bottom = height; - if (down) { - int count = getChildCount(); - if (count > 0) { - View view = getChildAt(count - 1); - mTempRect.bottom = view.getBottom(); - mTempRect.top = mTempRect.bottom - height; - } - } - return scrollAndFocus(direction, mTempRect.top, mTempRect.bottom, 0, 0, 0); - } else { - boolean right = direction == View.FOCUS_DOWN; - int width = getWidth(); - mTempRect.left = 0; - mTempRect.right = width; - if (right) { - int count = getChildCount(); - if (count > 0) { - View view = getChildAt(count - 1); - mTempRect.right = view.getBottom(); - mTempRect.left = mTempRect.right - width; - } - } - return scrollAndFocus(0, 0, 0, direction, mTempRect.top, mTempRect.bottom); - } - } - - /** - *

Scrolls the view to make the area defined by top and - * bottom visible. This method attempts to give the focus - * to a component visible in this area. If no component can be focused in - * the new visible area, the focus is reclaimed by this scrollview.

- * - * @param direction the scroll direction: {@link android.view.View#FOCUS_UP} - * to go upward - * {@link android.view.View#FOCUS_DOWN} to downward - * @param top the top offset of the new area to be made visible - * @param bottom the bottom offset of the new area to be made visible - * @return true if the key event is consumed by this method, false otherwise - */ - private boolean scrollAndFocus(int directionY, int top, int bottom, int directionX, int left, int right) { - boolean handled = true; - int height = getHeight(); - int containerTop = getScrollY(); - int containerBottom = containerTop + height; - boolean up = directionY == View.FOCUS_UP; - int width = getWidth(); - int containerLeft = getScrollX(); - int containerRight = containerLeft + width; - boolean leftwards = directionX == View.FOCUS_UP; - View newFocused = findFocusableViewInBounds(up, top, bottom, leftwards, left, right); - if (newFocused == null) { - newFocused = this; - } - if ((top >= containerTop && bottom <= containerBottom) || (left >= containerLeft && right <= containerRight)) { - handled = false; - } else { - int deltaY = up ? (top - containerTop) : (bottom - containerBottom); - int deltaX = leftwards ? (left - containerLeft) : (right - containerRight); - doScroll(deltaX, deltaY); - } - if (newFocused != findFocus() && newFocused.requestFocus(directionY)) { - mTwoDScrollViewMovedFocus = true; - mTwoDScrollViewMovedFocus = false; - } - return handled; - } - - /** - * Handle scrolling in response to an up or down arrow click. - * - * @param direction The direction corresponding to the arrow key that was - * pressed - * @return True if we consumed the event, false otherwise - */ - public boolean arrowScroll(int direction, boolean horizontal) { - View currentFocused = findFocus(); - if (currentFocused == this) currentFocused = null; - View nextFocused = FocusFinder.getInstance().findNextFocus(this, currentFocused, direction); - final int maxJump = horizontal ? getMaxScrollAmountHorizontal() : getMaxScrollAmountVertical(); - - if (!horizontal) { - if (nextFocused != null) { - nextFocused.getDrawingRect(mTempRect); - offsetDescendantRectToMyCoords(nextFocused, mTempRect); - int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect); - doScroll(0, scrollDelta); - nextFocused.requestFocus(direction); - } else { - // no new focus - int scrollDelta = maxJump; - if (direction == View.FOCUS_UP && getScrollY() < scrollDelta) { - scrollDelta = getScrollY(); - } else if (direction == View.FOCUS_DOWN) { - if (getChildCount() > 0) { - int daBottom = getChildAt(0).getBottom(); - int screenBottom = getScrollY() + getHeight(); - if (daBottom - screenBottom < maxJump) { - scrollDelta = daBottom - screenBottom; - } - } - } - if (scrollDelta == 0) { - return false; - } - doScroll(0, direction == View.FOCUS_DOWN ? scrollDelta : -scrollDelta); - } - } else { - if (nextFocused != null) { - nextFocused.getDrawingRect(mTempRect); - offsetDescendantRectToMyCoords(nextFocused, mTempRect); - int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect); - doScroll(scrollDelta, 0); - nextFocused.requestFocus(direction); - } else { - // no new focus - int scrollDelta = maxJump; - if (direction == View.FOCUS_UP && getScrollY() < scrollDelta) { - scrollDelta = getScrollY(); - } else if (direction == View.FOCUS_DOWN) { - if (getChildCount() > 0) { - int daBottom = getChildAt(0).getBottom(); - int screenBottom = getScrollY() + getHeight(); - if (daBottom - screenBottom < maxJump) { - scrollDelta = daBottom - screenBottom; - } - } - } - if (scrollDelta == 0) { - return false; - } - doScroll(direction == View.FOCUS_DOWN ? scrollDelta : -scrollDelta, 0); - } - } - return true; - } - - /** - * Smooth scroll by a Y delta - * - * @param delta the number of pixels to scroll by on the Y axis - */ - private void doScroll(int deltaX, int deltaY) { - if (deltaX != 0 || deltaY != 0) { - smoothScrollBy(deltaX, deltaY); - } - } - - /** - * Like {@link View#scrollBy}, but scroll smoothly instead of immediately. - * - * @param dx the number of pixels to scroll by on the X axis - * @param dy the number of pixels to scroll by on the Y axis - */ - public final void smoothScrollBy(int dx, int dy) { - long duration = AnimationUtils.currentAnimationTimeMillis() - mLastScroll; - if (duration > ANIMATED_SCROLL_GAP) { - mScroller.startScroll(getScrollX(), getScrollY(), dx, dy); - awakenScrollBars(mScroller.getDuration()); - invalidate(); - } else { - if (!mScroller.isFinished()) { - mScroller.abortAnimation(); - } - scrollBy(dx, dy); - } - mLastScroll = AnimationUtils.currentAnimationTimeMillis(); - } - - /** - * Like {@link #scrollTo}, but scroll smoothly instead of immediately. - * - * @param x the position where to scroll on the X axis - * @param y the position where to scroll on the Y axis - */ - public final void smoothScrollTo(int x, int y) { - smoothScrollBy(x - getScrollX(), y - getScrollY()); - } - - /** - *

The scroll range of a scroll view is the overall height of all of its - * children.

- */ - @Override - protected int computeVerticalScrollRange() { - int count = getChildCount(); - return count == 0 ? getHeight() : (getChildAt(0)).getBottom(); - } - - @Override - protected int computeHorizontalScrollRange() { - int count = getChildCount(); - return count == 0 ? getWidth() : (getChildAt(0)).getRight(); - } - - @Override - protected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) { - ViewGroup.LayoutParams lp = child.getLayoutParams(); - int childWidthMeasureSpec; - int childHeightMeasureSpec; - - childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, getPaddingLeft() + getPaddingRight(), lp.width); - childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); - - child.measure(childWidthMeasureSpec, childHeightMeasureSpec); - } - - @Override - protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { - final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); - final int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.leftMargin + lp.rightMargin, MeasureSpec.UNSPECIFIED); - final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(lp.topMargin + lp.bottomMargin, MeasureSpec.UNSPECIFIED); - - child.measure(childWidthMeasureSpec, childHeightMeasureSpec); - } - - @Override - public void computeScroll() { - if (mScroller.computeScrollOffset()) { - // This is called at drawing time by ViewGroup. We don't want to - // re-show the scrollbars at this point, which scrollTo will do, - // so we replicate most of scrollTo here. - // - // It's a little odd to call onScrollChanged from inside the drawing. - // - // It is, except when you remember that computeScroll() is used to - // animate scrolling. So unless we want to defer the onScrollChanged() - // until the end of the animated scrolling, we don't really have a - // choice here. - // - // I agree. The alternative, which I think would be worse, is to post - // something and tell the subclasses later. This is bad because there - // will be a window where mScrollX/Y is different from what the app - // thinks it is. - // - int oldX = getScrollX(); - int oldY = getScrollY(); - int x = mScroller.getCurrX(); - int y = mScroller.getCurrY(); - if (getChildCount() > 0) { - View child = getChildAt(0); - scrollTo(clamp(x, getWidth() - getPaddingRight() - getPaddingLeft(), child.getWidth()), - clamp(y, getHeight() - getPaddingBottom() - getPaddingTop(), child.getHeight())); - } else { - scrollTo(x, y); - } - if (oldX != getScrollX() || oldY != getScrollY()) { - onScrollChanged(getScrollX(), getScrollY(), oldX, oldY); - } - - // Keep on drawing until the animation has finished. - postInvalidate(); - } - } - - /** - * Scrolls the view to the given child. - * - * @param child the View to scroll to - */ - private void scrollToChild(View child) { - child.getDrawingRect(mTempRect); - /* Offset from child's local coordinates to TwoDScrollView coordinates */ - offsetDescendantRectToMyCoords(child, mTempRect); - int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect); - if (scrollDelta != 0) { - scrollBy(0, scrollDelta); - } - } - - /** - * If rect is off screen, scroll just enough to get it (or at least the - * first screen size chunk of it) on screen. - * - * @param rect The rectangle. - * @param immediate True to scroll immediately without animation - * @return true if scrolling was performed - */ - private boolean scrollToChildRect(Rect rect, boolean immediate) { - final int delta = computeScrollDeltaToGetChildRectOnScreen(rect); - final boolean scroll = delta != 0; - if (scroll) { - if (immediate) { - scrollBy(0, delta); - } else { - smoothScrollBy(0, delta); - } - } - return scroll; - } - - /** - * Compute the amount to scroll in the Y direction in order to get - * a rectangle completely on the screen (or, if taller than the screen, - * at least the first screen size chunk of it). - * - * @param rect The rect. - * @return The scroll delta. - */ - protected int computeScrollDeltaToGetChildRectOnScreen(Rect rect) { - if (getChildCount() == 0) return 0; - int height = getHeight(); - int screenTop = getScrollY(); - int screenBottom = screenTop + height; - int fadingEdge = getVerticalFadingEdgeLength(); - // leave room for top fading edge as long as rect isn't at very top - if (rect.top > 0) { - screenTop += fadingEdge; - } - - // leave room for bottom fading edge as long as rect isn't at very bottom - if (rect.bottom < getChildAt(0).getHeight()) { - screenBottom -= fadingEdge; - } - int scrollYDelta = 0; - if (rect.bottom > screenBottom && rect.top > screenTop) { - // need to move down to get it in view: move down just enough so - // that the entire rectangle is in view (or at least the first - // screen size chunk). - if (rect.height() > height) { - // just enough to get screen size chunk on - scrollYDelta += (rect.top - screenTop); - } else { - // get entire rect at bottom of screen - scrollYDelta += (rect.bottom - screenBottom); - } - - // make sure we aren't scrolling beyond the end of our content - int bottom = getChildAt(0).getBottom(); - int distanceToBottom = bottom - screenBottom; - scrollYDelta = Math.min(scrollYDelta, distanceToBottom); - - } else if (rect.top < screenTop && rect.bottom < screenBottom) { - // need to move up to get it in view: move up just enough so that - // entire rectangle is in view (or at least the first screen - // size chunk of it). - - if (rect.height() > height) { - // screen size chunk - scrollYDelta -= (screenBottom - rect.bottom); - } else { - // entire rect at top - scrollYDelta -= (screenTop - rect.top); - } - - // make sure we aren't scrolling any further than the top our content - scrollYDelta = Math.max(scrollYDelta, -getScrollY()); - } - return scrollYDelta; - } - - @Override - public void requestChildFocus(View child, View focused) { - if (!mTwoDScrollViewMovedFocus) { - if (!mIsLayoutDirty) { - scrollToChild(focused); - } else { - // The child may not be laid out yet, we can't compute the scroll yet - mChildToScrollTo = focused; - } - } - super.requestChildFocus(child, focused); - } - - /** - * When looking for focus in children of a scroll view, need to be a little - * more careful not to give focus to something that is scrolled off screen. - *

- * This is more expensive than the default {@link android.view.ViewGroup} - * implementation, otherwise this behavior might have been made the default. - */ - @Override - protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { - // convert from forward / backward notation to up / down / left / right - // (ugh). - if (direction == View.FOCUS_FORWARD) { - direction = View.FOCUS_DOWN; - } else if (direction == View.FOCUS_BACKWARD) { - direction = View.FOCUS_UP; - } - - final View nextFocus = previouslyFocusedRect == null ? - FocusFinder.getInstance().findNextFocus(this, null, direction) : - FocusFinder.getInstance().findNextFocusFromRect(this, - previouslyFocusedRect, direction); - - if (nextFocus == null) { - return false; - } - - return nextFocus.requestFocus(direction, previouslyFocusedRect); - } - - @Override - public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) { - // offset into coordinate space of this scroll view - rectangle.offset(child.getLeft() - child.getScrollX(), child.getTop() - child.getScrollY()); - return scrollToChildRect(rectangle, immediate); - } - - @Override - public void requestLayout() { - mIsLayoutDirty = true; - super.requestLayout(); - } - - @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) { - super.onLayout(changed, l, t, r, b); - mIsLayoutDirty = false; - // Give a child focus if it needs it - if (mChildToScrollTo != null && isViewDescendantOf(mChildToScrollTo, this)) { - scrollToChild(mChildToScrollTo); - } - mChildToScrollTo = null; - - // Calling this with the present values causes it to re-clam them - scrollTo(getScrollX(), getScrollY()); - } - - @Override - protected void onSizeChanged(int w, int h, int oldw, int oldh) { - super.onSizeChanged(w, h, oldw, oldh); - - View currentFocused = findFocus(); - if (null == currentFocused || this == currentFocused) - return; - - // If the currently-focused view was visible on the screen when the - // screen was at the old height, then scroll the screen to make that - // view visible with the new screen height. - currentFocused.getDrawingRect(mTempRect); - offsetDescendantRectToMyCoords(currentFocused, mTempRect); - int scrollDeltaX = computeScrollDeltaToGetChildRectOnScreen(mTempRect); - int scrollDeltaY = computeScrollDeltaToGetChildRectOnScreen(mTempRect); - doScroll(scrollDeltaX, scrollDeltaY); - } - - /** - * Return true if child is an descendant of parent, (or equal to the parent). - */ - private boolean isViewDescendantOf(View child, View parent) { - if (child == parent) { - return true; - } - - final ViewParent theParent = child.getParent(); - return (theParent instanceof ViewGroup) && isViewDescendantOf((View) theParent, parent); - } - - /** - * Fling the scroll view - * - * @param velocityY The initial velocity in the Y direction. Positive - * numbers mean that the finger/curor is moving down the screen, - * which means we want to scroll towards the top. - */ - public void fling(int velocityX, int velocityY) { - if (getChildCount() > 0) { - int height = getHeight() - getPaddingBottom() - getPaddingTop(); - int bottom = getChildAt(0).getHeight(); - int width = getWidth() - getPaddingRight() - getPaddingLeft(); - int right = getChildAt(0).getWidth(); - - mScroller.fling(getScrollX(), getScrollY(), velocityX, velocityY, 0, right - width, 0, bottom - height); - - final boolean movingDown = velocityY > 0; - final boolean movingRight = velocityX > 0; - - View newFocused = findFocusableViewInMyBounds(movingRight, mScroller.getFinalX(), movingDown, mScroller.getFinalY(), findFocus()); - if (newFocused == null) { - newFocused = this; - } - - if (newFocused != findFocus() && newFocused.requestFocus(movingDown ? View.FOCUS_DOWN : View.FOCUS_UP)) { - mTwoDScrollViewMovedFocus = true; - mTwoDScrollViewMovedFocus = false; - } - - awakenScrollBars(mScroller.getDuration()); - invalidate(); - } - } - - /** - * {@inheritDoc} - *

- *

This version also clamps the scrolling to the bounds of our child. - */ - public void scrollTo(int x, int y) { - // we rely on the fact the View.scrollBy calls scrollTo. - if (getChildCount() > 0) { - View child = getChildAt(0); - x = clamp(x, getWidth() - getPaddingRight() - getPaddingLeft(), child.getWidth()); - y = clamp(y, getHeight() - getPaddingBottom() - getPaddingTop(), child.getHeight()); - if (x != getScrollX() || y != getScrollY()) { - super.scrollTo(x, y); - } - } - } - - private int clamp(int n, int my, int child) { - if (my >= child || n < 0) { - /* my >= child is this case: - * |--------------- me ---------------| - * |------ child ------| - * or - * |--------------- me ---------------| - * |------ child ------| - * or - * |--------------- me ---------------| - * |------ child ------| - * - * n < 0 is this case: - * |------ me ------| - * |-------- child --------| - * |-- mScrollX --| - */ - return 0; - } - if ((my + n) > child) { - /* this case: - * |------ me ------| - * |------ child ------| - * |-- mScrollX --| - */ - return child - my; - } - return n; - } -} diff --git a/library/src/main/java/com/unnamed/b/atv/view/TwoDScrollView.kt b/library/src/main/java/com/unnamed/b/atv/view/TwoDScrollView.kt new file mode 100644 index 0000000..ac68edd --- /dev/null +++ b/library/src/main/java/com/unnamed/b/atv/view/TwoDScrollView.kt @@ -0,0 +1,1089 @@ +package com.unnamed.b.atv.view + + +import android.content.Context +import android.graphics.Rect +import android.util.AttributeSet +import android.view.* +import android.view.animation.AnimationUtils +import android.widget.FrameLayout +import android.widget.LinearLayout +import android.widget.Scroller +import android.widget.TextView + +/** + * Layout container for a view hierarchy that can be scrolled by the user, + * allowing it to be larger than the physical display. A TwoDScrollView + * is a [FrameLayout], meaning you should place one child in it + * containing the entire contents to scroll; this child may itself be a layout + * manager with a complex hierarchy of objects. A child that is often used + * is a [LinearLayout] in a vertical orientation, presenting a vertical + * array of top-level items that the user can scroll through. + * + * + * + * The [TextView] class also + * takes care of its own scrolling, so does not require a TwoDScrollView, but + * using the two together is possible to achieve the effect of a text view + * within a larger container. + * Converted to Kolin by Kumar Shivang on 16/07/17 + */ +class TwoDScrollView : FrameLayout { + + private var mLastScroll: Long = 0 + + private val mTempRect = Rect() + private var mScroller: Scroller? = null + + /** + * Flag to indicate that we are moving focus ourselves. This is so the + * code that watches for focus changes initiated outside this TwoDScrollView + * knows that it does not have to do anything. + */ + private var mTwoDScrollViewMovedFocus: Boolean = false + + /** + * Position of the last motion event. + */ + private var mLastMotionY: Float = 0.toFloat() + private var mLastMotionX: Float = 0.toFloat() + + /** + * True when the layout has changed but the traversal has not come through yet. + * Ideally the view hierarchy would keep track of this for us. + */ + private var mIsLayoutDirty = true + + /** + * The child to give focus to in the event that a child has requested focus while the + * layout is dirty. This prevents the scroll from being wrong if the child has not been + * laid out before requesting focus. + */ + private var mChildToScrollTo: View? = null + + /** + * True if the user is currently dragging this TwoDScrollView around. This is + * not the same as 'is being flinged', which can be checked by + * mScroller.isFinished() (flinging begins when the user lifts his finger). + */ + private var mIsBeingDragged = false + + /** + * Determines speed during touch scrolling + */ + private var mVelocityTracker: VelocityTracker? = null + + /** + * Whether arrow scrolling is animated. + */ + private var mTouchSlop: Int = 0 + private var mMinimumVelocity: Int = 0 + private var mMaximumVelocity: Int = 0 + + constructor(context: Context) : super(context) { + initTwoDScrollView() + } + + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { + initTwoDScrollView() + } + + constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) { + initTwoDScrollView() + } + + override fun getTopFadingEdgeStrength(): Float { + if (childCount == 0) { + return 0.0f + } + val length = verticalFadingEdgeLength + if (scrollY < length) { + return scrollY / length.toFloat() + } + return 1.0f + } + + override fun getBottomFadingEdgeStrength(): Float { + if (childCount == 0) { + return 0.0f + } + val length = verticalFadingEdgeLength + val bottomEdge = height - paddingBottom + val span = getChildAt(0).bottom - scrollY - bottomEdge + if (span < length) { + return span / length.toFloat() + } + return 1.0f + } + + override fun getLeftFadingEdgeStrength(): Float { + if (childCount == 0) { + return 0.0f + } + val length = horizontalFadingEdgeLength + if (scrollX < length) { + return scrollX / length.toFloat() + } + return 1.0f + } + + override fun getRightFadingEdgeStrength(): Float { + if (childCount == 0) { + return 0.0f + } + val length = horizontalFadingEdgeLength + val rightEdge = width - paddingRight + val span = getChildAt(0).right - scrollX - rightEdge + if (span < length) { + return span / length.toFloat() + } + return 1.0f + } + + /** + * @return The maximum amount this scroll view will scroll in response to + * * an arrow event. + */ + val maxScrollAmountVertical: Int + get() = (MAX_SCROLL_FACTOR * height).toInt() + + val maxScrollAmountHorizontal: Int + get() = (MAX_SCROLL_FACTOR * width).toInt() + + private fun initTwoDScrollView() { + mScroller = Scroller(context) + isFocusable = true + descendantFocusability = ViewGroup.FOCUS_AFTER_DESCENDANTS + setWillNotDraw(false) + val configuration = ViewConfiguration.get(context) + mTouchSlop = configuration.scaledTouchSlop + mMinimumVelocity = configuration.scaledMinimumFlingVelocity + mMaximumVelocity = configuration.scaledMaximumFlingVelocity + } + + override fun addView(child: View) { + if (childCount > 0) { + throw IllegalStateException("TwoDScrollView can host only one direct child") + } + super.addView(child) + } + + override fun addView(child: View, index: Int) { + if (childCount > 0) { + throw IllegalStateException("TwoDScrollView can host only one direct child") + } + super.addView(child, index) + } + + override fun addView(child: View, params: ViewGroup.LayoutParams) { + if (childCount > 0) { + throw IllegalStateException("TwoDScrollView can host only one direct child") + } + super.addView(child, params) + } + + override fun addView(child: View, index: Int, params: ViewGroup.LayoutParams) { + if (childCount > 0) { + throw IllegalStateException("TwoDScrollView can host only one direct child") + } + super.addView(child, index, params) + } + + /** + * @return Returns true this TwoDScrollView can be scrolled + */ + private fun canScroll(): Boolean { + val child = getChildAt(0) + if (child != null) { + val childHeight = child.height + val childWidth = child.width + return height < childHeight + paddingTop + paddingBottom || width < childWidth + paddingLeft + paddingRight + } + return false + } + + override fun dispatchKeyEvent(event: KeyEvent): Boolean { + // Let the focused view and/or our descendants get the key first + val handled = super.dispatchKeyEvent(event) + if (handled) { + return true + } + return executeKeyEvent(event) + } + + /** + * You can call this function yourself to have the scroll view perform + * scrolling from a key event, just as if the event had been dispatched to + * it by the view hierarchy. + + * @param event The key event to execute. + * * + * @return Return true if the event was handled, else false. + */ + fun executeKeyEvent(event: KeyEvent): Boolean { + mTempRect.setEmpty() + if (!canScroll()) { + if (isFocused) { + var currentFocused: View? = findFocus() + if (currentFocused === this) currentFocused = null + val nextFocused = FocusFinder.getInstance().findNextFocus(this, currentFocused, View.FOCUS_DOWN) + return nextFocused != null && nextFocused !== this && nextFocused.requestFocus(View.FOCUS_DOWN) + } + return false + } + var handled = false + if (event.action == KeyEvent.ACTION_DOWN) { + when (event.keyCode) { + KeyEvent.KEYCODE_DPAD_UP -> if (!event.isAltPressed) { + handled = arrowScroll(View.FOCUS_UP, false) + } else { + handled = fullScroll(View.FOCUS_UP, false) + } + KeyEvent.KEYCODE_DPAD_DOWN -> if (!event.isAltPressed) { + handled = arrowScroll(View.FOCUS_DOWN, false) + } else { + handled = fullScroll(View.FOCUS_DOWN, false) + } + KeyEvent.KEYCODE_DPAD_LEFT -> if (!event.isAltPressed) { + handled = arrowScroll(View.FOCUS_LEFT, true) + } else { + handled = fullScroll(View.FOCUS_LEFT, true) + } + KeyEvent.KEYCODE_DPAD_RIGHT -> if (!event.isAltPressed) { + handled = arrowScroll(View.FOCUS_RIGHT, true) + } else { + handled = fullScroll(View.FOCUS_RIGHT, true) + } + } + } + return handled + } + + override fun onInterceptTouchEvent(ev: MotionEvent): Boolean { + /* + * This method JUST determines whether we want to intercept the motion. + * If we return true, onMotionEvent will be called and we do the actual + * scrolling there. + * + * Shortcut the most recurring case: the user is in the dragging + * state and he is moving his finger. We want to intercept this + * motion. + */ + val action = ev.action + if (action == MotionEvent.ACTION_MOVE && mIsBeingDragged) { + return true + } + if (!canScroll()) { + mIsBeingDragged = false + return false + } + val y = ev.y + val x = ev.x + when (action) { + MotionEvent.ACTION_MOVE -> { + /* + * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check + * whether the user has moved far enough from his original down touch. + */ + /* + * Locally do absolute value. mLastMotionY is set to the y value + * of the down event. + */ + val yDiff = Math.abs(y - mLastMotionY).toInt() + val xDiff = Math.abs(x - mLastMotionX).toInt() + if (yDiff > mTouchSlop || xDiff > mTouchSlop) { + mIsBeingDragged = true + } + } + + MotionEvent.ACTION_DOWN -> { + /* Remember location of down touch */ + mLastMotionY = y + mLastMotionX = x + + /* + * If being flinged and user touches the screen, initiate drag; + * otherwise don't. mScroller.isFinished should be false when + * being flinged. + */ + mIsBeingDragged = !mScroller!!.isFinished + } + + MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_UP -> + /* Release the drag */ + mIsBeingDragged = false + } + + /* + * The only time we want to intercept motion events is if we are in the + * drag mode. + */ + return mIsBeingDragged + } + + override fun onTouchEvent(ev: MotionEvent): Boolean { + + if (ev.action == MotionEvent.ACTION_DOWN && ev.edgeFlags != 0) { + // Don't handle edge touches immediately -- they may actually belong to one of our + // descendants. + return false + } + + if (!canScroll()) { + return false + } + + if (mVelocityTracker == null) { + mVelocityTracker = VelocityTracker.obtain() + } + mVelocityTracker!!.addMovement(ev) + + val action = ev.action + val y = ev.y + val x = ev.x + + when (action) { + MotionEvent.ACTION_DOWN -> { + /* + * If being flinged and user touches, stop the fling. isFinished + * will be false if being flinged. + */ + if (!mScroller!!.isFinished) { + mScroller!!.abortAnimation() + } + + // Remember where the motion event started + mLastMotionY = y + mLastMotionX = x + } + MotionEvent.ACTION_MOVE -> { + // Scroll to follow the motion event + var deltaX = (mLastMotionX - x).toInt() + var deltaY = (mLastMotionY - y).toInt() + mLastMotionX = x + mLastMotionY = y + + if (deltaX < 0) { + if (scrollX < 0) { + deltaX = 0 + } + } else if (deltaX > 0) { + val rightEdge = width - paddingRight + val availableToScroll = getChildAt(0).right - scrollX - rightEdge + if (availableToScroll > 0) { + deltaX = Math.min(availableToScroll, deltaX) + } else { + deltaX = 0 + } + } + if (deltaY < 0) { + if (scrollY < 0) { + deltaY = 0 + } + } else if (deltaY > 0) { + val bottomEdge = height - paddingBottom + val availableToScroll = getChildAt(0).bottom - scrollY - bottomEdge + if (availableToScroll > 0) { + deltaY = Math.min(availableToScroll, deltaY) + } else { + deltaY = 0 + } + } + if (deltaY != 0 || deltaX != 0) + scrollBy(deltaX, deltaY) + } + MotionEvent.ACTION_UP -> { + val velocityTracker = mVelocityTracker + velocityTracker!!.computeCurrentVelocity(1000, mMaximumVelocity.toFloat()) + val initialXVelocity = velocityTracker.xVelocity.toInt() + val initialYVelocity = velocityTracker.yVelocity.toInt() + if (Math.abs(initialXVelocity) + Math.abs(initialYVelocity) > mMinimumVelocity && childCount > 0) { + fling(-initialXVelocity, -initialYVelocity) + } + if (mVelocityTracker != null) { + mVelocityTracker!!.recycle() + mVelocityTracker = null + } + } + } + return true + } + + /** + * Finds the next focusable component that fits in this View's bounds + * (excluding fading edges) pretending that this View's top is located at + * the parameter top. + + * @param topFocus look for a candidate is the one at the top of the bounds + * * if topFocus is true, or at the bottom of the bounds if topFocus is + * * false + * * + * @param top the top offset of the bounds in which a focusable must be + * * found (the fading edge is assumed to start at this position) + * * + * @param preferredFocusable the View that has highest priority and will be + * * returned if it is within my bounds (null is valid) + * * + * @return the next focusable component in the bounds or null if none can be + * * found + */ + private fun findFocusableViewInMyBounds(topFocus: Boolean, top: Int, leftFocus: Boolean, left: Int, preferredFocusable: View?): View? { + /* + * The fading edge's transparent side should be considered for focus + * since it's mostly visible, so we divide the actual fading edge length + * by 2. + */ + val verticalFadingEdgeLength = verticalFadingEdgeLength / 2 + val topWithoutFadingEdge = top + verticalFadingEdgeLength + val bottomWithoutFadingEdge = top + height - verticalFadingEdgeLength + val horizontalFadingEdgeLength = horizontalFadingEdgeLength / 2 + val leftWithoutFadingEdge = left + horizontalFadingEdgeLength + val rightWithoutFadingEdge = left + width - horizontalFadingEdgeLength + + if (preferredFocusable != null + && preferredFocusable.top < bottomWithoutFadingEdge + && preferredFocusable.bottom > topWithoutFadingEdge + && preferredFocusable.left < rightWithoutFadingEdge + && preferredFocusable.right > leftWithoutFadingEdge) { + return preferredFocusable + } + return findFocusableViewInBounds(topFocus, topWithoutFadingEdge, bottomWithoutFadingEdge, leftFocus, leftWithoutFadingEdge, rightWithoutFadingEdge) + } + + /** + * Finds the next focusable component that fits in the specified bounds. + * + + * @param topFocus look for a candidate is the one at the top of the bounds + * * if topFocus is true, or at the bottom of the bounds if topFocus is + * * false + * * + * @param top the top offset of the bounds in which a focusable must be + * * found + * * + * @param bottom the bottom offset of the bounds in which a focusable must + * * be found + * * + * @return the next focusable component in the bounds or null if none can + * * be found + */ + private fun findFocusableViewInBounds(topFocus: Boolean, top: Int, bottom: Int, leftFocus: Boolean, left: Int, right: Int): View? { + val focusables = getFocusables(View.FOCUS_FORWARD) + var focusCandidate: View? = null + + /* + * A fully contained focusable is one where its top is below the bound's + * top, and its bottom is above the bound's bottom. A partially + * contained focusable is one where some part of it is within the + * bounds, but it also has some part that is not within bounds. A fully contained + * focusable is preferred to a partially contained focusable. + */ + var foundFullyContainedFocusable = false + + val count = focusables.size + for (i in 0..count - 1) { + val view = focusables[i] + val viewTop = view.top + val viewBottom = view.bottom + val viewLeft = view.left + val viewRight = view.right + + if (top < viewBottom && viewTop < bottom && left < viewRight && viewLeft < right) { + /* + * the focusable is in the target area, it is a candidate for + * focusing + */ + val viewIsFullyContained = top < viewTop && viewBottom < bottom && left < viewLeft && viewRight < right + if (focusCandidate == null) { + /* No candidate, take this one */ + focusCandidate = view + foundFullyContainedFocusable = viewIsFullyContained + } else { + val viewIsCloserToVerticalBoundary = topFocus && viewTop < focusCandidate.top || !topFocus && viewBottom > focusCandidate.bottom + val viewIsCloserToHorizontalBoundary = leftFocus && viewLeft < focusCandidate.left || !leftFocus && viewRight > focusCandidate.right + if (foundFullyContainedFocusable) { + if (viewIsFullyContained && viewIsCloserToVerticalBoundary && viewIsCloserToHorizontalBoundary) { + /* + * We're dealing with only fully contained views, so + * it has to be closer to the boundary to beat our + * candidate + */ + focusCandidate = view + } + } else { + if (viewIsFullyContained) { + /* Any fully contained view beats a partially contained view */ + focusCandidate = view + foundFullyContainedFocusable = true + } else if (viewIsCloserToVerticalBoundary && viewIsCloserToHorizontalBoundary) { + /* + * Partially contained view beats another partially + * contained view if it's closer + */ + focusCandidate = view + } + } + } + } + } + return focusCandidate + } + + /** + * + * Handles scrolling in response to a "home/end" shortcut press. This + * method will scroll the view to the top or bottom and give the focus + * to the topmost/bottommost component in the new visible area. If no + * component is a good candidate for focus, this scrollview reclaims the + * focus. + + * @param direction the scroll direction: [android.view.View.FOCUS_UP] + * * to go the top of the view or + * * [android.view.View.FOCUS_DOWN] to go the bottom + * * + * @return true if the key event is consumed by this method, false otherwise + */ + fun fullScroll(direction: Int, horizontal: Boolean): Boolean { + if (!horizontal) { + val down = direction == View.FOCUS_DOWN + val height = height + mTempRect.top = 0 + mTempRect.bottom = height + if (down) { + val count = childCount + if (count > 0) { + val view = getChildAt(count - 1) + mTempRect.bottom = view.bottom + mTempRect.top = mTempRect.bottom - height + } + } + return scrollAndFocus(direction, mTempRect.top, mTempRect.bottom, 0, 0, 0) + } else { + val right = direction == View.FOCUS_DOWN + val width = width + mTempRect.left = 0 + mTempRect.right = width + if (right) { + val count = childCount + if (count > 0) { + val view = getChildAt(count - 1) + mTempRect.right = view.bottom + mTempRect.left = mTempRect.right - width + } + } + return scrollAndFocus(0, 0, 0, direction, mTempRect.top, mTempRect.bottom) + } + } + + /** + * + * Scrolls the view to make the area defined by `top` and + * `bottom` visible. This method attempts to give the focus + * to a component visible in this area. If no component can be focused in + * the new visible area, the focus is reclaimed by this scrollview. + + * @param direction the scroll direction: [android.view.View.FOCUS_UP] + * * to go upward + * * [android.view.View.FOCUS_DOWN] to downward + * * + * @param top the top offset of the new area to be made visible + * * + * @param bottom the bottom offset of the new area to be made visible + * * + * @return true if the key event is consumed by this method, false otherwise + */ + private fun scrollAndFocus(directionY: Int, top: Int, bottom: Int, directionX: Int, left: Int, right: Int): Boolean { + var handled = true + val height = height + val containerTop = scrollY + val containerBottom = containerTop + height + val up = directionY == View.FOCUS_UP + val width = width + val containerLeft = scrollX + val containerRight = containerLeft + width + val leftwards = directionX == View.FOCUS_UP + var newFocused: View? = findFocusableViewInBounds(up, top, bottom, leftwards, left, right) + if (newFocused == null) { + newFocused = this + } + if (top >= containerTop && bottom <= containerBottom || left >= containerLeft && right <= containerRight) { + handled = false + } else { + val deltaY = if (up) top - containerTop else bottom - containerBottom + val deltaX = if (leftwards) left - containerLeft else right - containerRight + doScroll(deltaX, deltaY) + } + if (newFocused !== findFocus() && newFocused.requestFocus(directionY)) { + mTwoDScrollViewMovedFocus = true + mTwoDScrollViewMovedFocus = false + } + return handled + } + + /** + * Handle scrolling in response to an up or down arrow click. + + * @param direction The direction corresponding to the arrow key that was + * * pressed + * * + * @return True if we consumed the event, false otherwise + */ + fun arrowScroll(direction: Int, horizontal: Boolean): Boolean { + var currentFocused: View? = findFocus() + if (currentFocused === this) currentFocused = null + val nextFocused = FocusFinder.getInstance().findNextFocus(this, currentFocused, direction) + val maxJump = if (horizontal) maxScrollAmountHorizontal else maxScrollAmountVertical + + if (!horizontal) { + if (nextFocused != null) { + nextFocused.getDrawingRect(mTempRect) + offsetDescendantRectToMyCoords(nextFocused, mTempRect) + val scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect) + doScroll(0, scrollDelta) + nextFocused.requestFocus(direction) + } else { + // no new focus + var scrollDelta = maxJump + if (direction == View.FOCUS_UP && scrollY < scrollDelta) { + scrollDelta = scrollY + } else if (direction == View.FOCUS_DOWN) { + if (childCount > 0) { + val daBottom = getChildAt(0).bottom + val screenBottom = scrollY + height + if (daBottom - screenBottom < maxJump) { + scrollDelta = daBottom - screenBottom + } + } + } + if (scrollDelta == 0) { + return false + } + doScroll(0, if (direction == View.FOCUS_DOWN) scrollDelta else -scrollDelta) + } + } else { + if (nextFocused != null) { + nextFocused.getDrawingRect(mTempRect) + offsetDescendantRectToMyCoords(nextFocused, mTempRect) + val scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect) + doScroll(scrollDelta, 0) + nextFocused.requestFocus(direction) + } else { + // no new focus + var scrollDelta = maxJump + if (direction == View.FOCUS_UP && scrollY < scrollDelta) { + scrollDelta = scrollY + } else if (direction == View.FOCUS_DOWN) { + if (childCount > 0) { + val daBottom = getChildAt(0).bottom + val screenBottom = scrollY + height + if (daBottom - screenBottom < maxJump) { + scrollDelta = daBottom - screenBottom + } + } + } + if (scrollDelta == 0) { + return false + } + doScroll(if (direction == View.FOCUS_DOWN) scrollDelta else -scrollDelta, 0) + } + } + return true + } + + /** + * Smooth scroll by a Y delta + + * @param delta the number of pixels to scroll by on the Y axis + */ + private fun doScroll(deltaX: Int, deltaY: Int) { + if (deltaX != 0 || deltaY != 0) { + smoothScrollBy(deltaX, deltaY) + } + } + + /** + * Like [View.scrollBy], but scroll smoothly instead of immediately. + + * @param dx the number of pixels to scroll by on the X axis + * * + * @param dy the number of pixels to scroll by on the Y axis + */ + fun smoothScrollBy(dx: Int, dy: Int) { + val duration = AnimationUtils.currentAnimationTimeMillis() - mLastScroll + if (duration > ANIMATED_SCROLL_GAP) { + mScroller!!.startScroll(scrollX, scrollY, dx, dy) + awakenScrollBars(mScroller!!.duration) + invalidate() + } else { + if (!mScroller!!.isFinished) { + mScroller!!.abortAnimation() + } + scrollBy(dx, dy) + } + mLastScroll = AnimationUtils.currentAnimationTimeMillis() + } + + /** + * Like [.scrollTo], but scroll smoothly instead of immediately. + + * @param x the position where to scroll on the X axis + * * + * @param y the position where to scroll on the Y axis + */ + fun smoothScrollTo(x: Int, y: Int) { + smoothScrollBy(x - scrollX, y - scrollY) + } + + /** + * + * The scroll range of a scroll view is the overall height of all of its + * children. + */ + override fun computeVerticalScrollRange(): Int { + val count = childCount + return if (count == 0) height else getChildAt(0).bottom + } + + override fun computeHorizontalScrollRange(): Int { + val count = childCount + return if (count == 0) width else getChildAt(0).right + } + + override fun measureChild(child: View, parentWidthMeasureSpec: Int, parentHeightMeasureSpec: Int) { + val lp = child.layoutParams + val childWidthMeasureSpec: Int + val childHeightMeasureSpec: Int + + childWidthMeasureSpec = ViewGroup.getChildMeasureSpec(parentWidthMeasureSpec, paddingLeft + paddingRight, lp.width) + childHeightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) + + child.measure(childWidthMeasureSpec, childHeightMeasureSpec) + } + + override fun measureChildWithMargins(child: View, parentWidthMeasureSpec: Int, widthUsed: Int, parentHeightMeasureSpec: Int, heightUsed: Int) { + val lp = child.layoutParams as ViewGroup.MarginLayoutParams + val childWidthMeasureSpec = View.MeasureSpec.makeMeasureSpec(lp.leftMargin + lp.rightMargin, View.MeasureSpec.UNSPECIFIED) + val childHeightMeasureSpec = View.MeasureSpec.makeMeasureSpec(lp.topMargin + lp.bottomMargin, View.MeasureSpec.UNSPECIFIED) + + child.measure(childWidthMeasureSpec, childHeightMeasureSpec) + } + + override fun computeScroll() { + if (mScroller!!.computeScrollOffset()) { + // This is called at drawing time by ViewGroup. We don't want to + // re-show the scrollbars at this point, which scrollTo will do, + // so we replicate most of scrollTo here. + // + // It's a little odd to call onScrollChanged from inside the drawing. + // + // It is, except when you remember that computeScroll() is used to + // animate scrolling. So unless we want to defer the onScrollChanged() + // until the end of the animated scrolling, we don't really have a + // choice here. + // + // I agree. The alternative, which I think would be worse, is to post + // something and tell the subclasses later. This is bad because there + // will be a window where mScrollX/Y is different from what the app + // thinks it is. + // + val oldX = scrollX + val oldY = scrollY + val x = mScroller!!.currX + val y = mScroller!!.currY + if (childCount > 0) { + val child = getChildAt(0) + scrollTo(clamp(x, width - paddingRight - paddingLeft, child.width), + clamp(y, height - paddingBottom - paddingTop, child.height)) + } else { + scrollTo(x, y) + } + if (oldX != scrollX || oldY != scrollY) { + onScrollChanged(scrollX, scrollY, oldX, oldY) + } + + // Keep on drawing until the animation has finished. + postInvalidate() + } + } + + /** + * Scrolls the view to the given child. + + * @param child the View to scroll to + */ + private fun scrollToChild(child: View) { + child.getDrawingRect(mTempRect) + /* Offset from child's local coordinates to TwoDScrollView coordinates */ + offsetDescendantRectToMyCoords(child, mTempRect) + val scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect) + if (scrollDelta != 0) { + scrollBy(0, scrollDelta) + } + } + + /** + * If rect is off screen, scroll just enough to get it (or at least the + * first screen size chunk of it) on screen. + + * @param rect The rectangle. + * * + * @param immediate True to scroll immediately without animation + * * + * @return true if scrolling was performed + */ + private fun scrollToChildRect(rect: Rect, immediate: Boolean): Boolean { + val delta = computeScrollDeltaToGetChildRectOnScreen(rect) + val scroll = delta != 0 + if (scroll) { + if (immediate) { + scrollBy(0, delta) + } else { + smoothScrollBy(0, delta) + } + } + return scroll + } + + /** + * Compute the amount to scroll in the Y direction in order to get + * a rectangle completely on the screen (or, if taller than the screen, + * at least the first screen size chunk of it). + + * @param rect The rect. + * * + * @return The scroll delta. + */ + protected fun computeScrollDeltaToGetChildRectOnScreen(rect: Rect): Int { + if (childCount == 0) return 0 + val height = height + var screenTop = scrollY + var screenBottom = screenTop + height + val fadingEdge = verticalFadingEdgeLength + // leave room for top fading edge as long as rect isn't at very top + if (rect.top > 0) { + screenTop += fadingEdge + } + + // leave room for bottom fading edge as long as rect isn't at very bottom + if (rect.bottom < getChildAt(0).height) { + screenBottom -= fadingEdge + } + var scrollYDelta = 0 + if (rect.bottom > screenBottom && rect.top > screenTop) { + // need to move down to get it in view: move down just enough so + // that the entire rectangle is in view (or at least the first + // screen size chunk). + if (rect.height() > height) { + // just enough to get screen size chunk on + scrollYDelta += rect.top - screenTop + } else { + // get entire rect at bottom of screen + scrollYDelta += rect.bottom - screenBottom + } + + // make sure we aren't scrolling beyond the end of our content + val bottom = getChildAt(0).bottom + val distanceToBottom = bottom - screenBottom + scrollYDelta = Math.min(scrollYDelta, distanceToBottom) + + } else if (rect.top < screenTop && rect.bottom < screenBottom) { + // need to move up to get it in view: move up just enough so that + // entire rectangle is in view (or at least the first screen + // size chunk of it). + + if (rect.height() > height) { + // screen size chunk + scrollYDelta -= screenBottom - rect.bottom + } else { + // entire rect at top + scrollYDelta -= screenTop - rect.top + } + + // make sure we aren't scrolling any further than the top our content + scrollYDelta = Math.max(scrollYDelta, -scrollY) + } + return scrollYDelta + } + + override fun requestChildFocus(child: View, focused: View) { + if (!mTwoDScrollViewMovedFocus) { + if (!mIsLayoutDirty) { + scrollToChild(focused) + } else { + // The child may not be laid out yet, we can't compute the scroll yet + mChildToScrollTo = focused + } + } + super.requestChildFocus(child, focused) + } + + /** + * When looking for focus in children of a scroll view, need to be a little + * more careful not to give focus to something that is scrolled off screen. + * + * + * This is more expensive than the default [android.view.ViewGroup] + * implementation, otherwise this behavior might have been made the default. + */ + override fun onRequestFocusInDescendants(direction: Int, previouslyFocusedRect: Rect?): Boolean { + var direction = direction + // convert from forward / backward notation to up / down / left / right + // (ugh). + if (direction == View.FOCUS_FORWARD) { + direction = View.FOCUS_DOWN + } else if (direction == View.FOCUS_BACKWARD) { + direction = View.FOCUS_UP + } + + val nextFocus = (if (previouslyFocusedRect == null) + FocusFinder.getInstance().findNextFocus(this, null, direction) + else + FocusFinder.getInstance().findNextFocusFromRect(this, + previouslyFocusedRect, direction)) ?: return false + + return nextFocus.requestFocus(direction, previouslyFocusedRect) + } + + override fun requestChildRectangleOnScreen(child: View, rectangle: Rect, immediate: Boolean): Boolean { + // offset into coordinate space of this scroll view + rectangle.offset(child.left - child.scrollX, child.top - child.scrollY) + return scrollToChildRect(rectangle, immediate) + } + + override fun requestLayout() { + mIsLayoutDirty = true + super.requestLayout() + } + + override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) { + super.onLayout(changed, l, t, r, b) + mIsLayoutDirty = false + // Give a child focus if it needs it + if (mChildToScrollTo != null && isViewDescendantOf(mChildToScrollTo!!, this)) { + scrollToChild(mChildToScrollTo!!) + } + mChildToScrollTo = null + + // Calling this with the present values causes it to re-clam them + scrollTo(scrollX, scrollY) + } + + override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { + super.onSizeChanged(w, h, oldw, oldh) + + val currentFocused = findFocus() + if (null == currentFocused || this === currentFocused) + return + + // If the currently-focused view was visible on the screen when the + // screen was at the old height, then scroll the screen to make that + // view visible with the new screen height. + currentFocused.getDrawingRect(mTempRect) + offsetDescendantRectToMyCoords(currentFocused, mTempRect) + val scrollDeltaX = computeScrollDeltaToGetChildRectOnScreen(mTempRect) + val scrollDeltaY = computeScrollDeltaToGetChildRectOnScreen(mTempRect) + doScroll(scrollDeltaX, scrollDeltaY) + } + + /** + * Return true if child is an descendant of parent, (or equal to the parent). + */ + private fun isViewDescendantOf(child: View, parent: View): Boolean { + if (child === parent) { + return true + } + + val theParent = child.parent + return theParent is ViewGroup && isViewDescendantOf(theParent as View, parent) + } + + /** + * Fling the scroll view + + * @param velocityY The initial velocity in the Y direction. Positive + * * numbers mean that the finger/curor is moving down the screen, + * * which means we want to scroll towards the top. + */ + fun fling(velocityX: Int, velocityY: Int) { + if (childCount > 0) { + val height = height - paddingBottom - paddingTop + val bottom = getChildAt(0).height + val width = width - paddingRight - paddingLeft + val right = getChildAt(0).width + + mScroller!!.fling(scrollX, scrollY, velocityX, velocityY, 0, right - width, 0, bottom - height) + + val movingDown = velocityY > 0 + val movingRight = velocityX > 0 + + var newFocused: View? = findFocusableViewInMyBounds(movingRight, mScroller!!.finalX, movingDown, mScroller!!.finalY, findFocus()) + if (newFocused == null) { + newFocused = this + } + + if (newFocused !== findFocus() && newFocused.requestFocus(if (movingDown) View.FOCUS_DOWN else View.FOCUS_UP)) { + mTwoDScrollViewMovedFocus = true + mTwoDScrollViewMovedFocus = false + } + + awakenScrollBars(mScroller!!.duration) + invalidate() + } + } + + /** + * {@inheritDoc} + * + * + * + * This version also clamps the scrolling to the bounds of our child. + */ + override fun scrollTo(x: Int, y: Int) { + var x = x + var y = y + // we rely on the fact the View.scrollBy calls scrollTo. + if (childCount > 0) { + val child = getChildAt(0) + x = clamp(x, width - paddingRight - paddingLeft, child.width) + y = clamp(y, height - paddingBottom - paddingTop, child.height) + if (x != scrollX || y != scrollY) { + super.scrollTo(x, y) + } + } + } + + private fun clamp(n: Int, my: Int, child: Int): Int { + if (my >= child || n < 0) { + /* my >= child is this case: + * |--------------- me ---------------| + * |------ child ------| + * or + * |--------------- me ---------------| + * |------ child ------| + * or + * |--------------- me ---------------| + * |------ child ------| + * + * n < 0 is this case: + * |------ me ------| + * |-------- child --------| + * |-- mScrollX --| + */ + return 0 + } + if (my + n > child) { + /* this case: + * |------ me ------| + * |------ child ------| + * |-- mScrollX --| + */ + return child - my + } + return n + } + + companion object { + internal val ANIMATED_SCROLL_GAP = 250 + internal val MAX_SCROLL_FACTOR = 0.5f + } +} From 11f067e31ccde38f258ed7bc358083f3f661c68a Mon Sep 17 00:00:00 2001 From: kshivang Date: Sun, 16 Jul 2017 03:21:34 +0530 Subject: [PATCH 2/2] Gradle updated 2.3.3 print library updated to latest updated activity to appCompatActivity updated fragment to support fragment java code converted to kotlin updated version to 1.3.0 updated readme file --- README.md | 59 ++++++++++++++++++++++++----------------------- gradle.properties | 4 ++-- 2 files changed, 32 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 89074e2..139b5b1 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ AndroidTreeView ### Recent changes +Entire library code converted to Kotlin for better stability and reducing null pointer errors though could be used with java as well + 2D scrolling mode added, keep in mind this comes with few limitations: you won't be able not place views on right side like alignParentRight. Everything should be align left. Is not enabled by default @@ -43,61 +45,60 @@ Tree view implementation for android **1)** Add library as a dependency to your project -```compile 'com.github.bmelnychuk:atv:1.2.+'``` +```compile 'com.github.bmelnychuk:atv:1.3.+'``` **2)** Create your tree starting from root element. ```TreeNode.root()``` element will not be displayed so it doesn't require anything to be set. -```java -TreeNode root = TreeNode.root(); +```Kotlin +val root = TreeNode.root() ``` Create and add your nodes (use your custom object as constructor param) -```java - TreeNode parent = new TreeNode("MyParentNode"); - TreeNode child0 = new TreeNode("ChildNode0"); - TreeNode child1 = new TreeNode("ChildNode1"); - parent.addChildren(child0, child1); - root.addChild(parent); +```Kotlin + val parent = TreeNode("MyParentNode") + val child0 = TreeNode("ChildNode0") + val child1 = TreeNode("ChildNode1") + parent.addChildren(child0, child1) + root.addChild(parent) ``` **3)** Add tree view to layout -```java - AndroidTreeView tView = new AndroidTreeView(getActivity(), root); - containerView.addView(tView.getView()); +```Kotlin + val tView = AndroidTreeView(getActivity(), root) + containerView.addView(tView.getView()) ``` The simplest but not styled tree is ready. Now you can see ```parent``` node as root of your tree **4)** Custom view for nodes Extend ```TreeNode.BaseNodeViewHolder``` and overwrite ```createNodeView``` method to prepare custom view for node: -```java -public class MyHolder extends TreeNode.BaseNodeViewHolder { +```Kotlin +class MyHolder : TreeNode.BaseNodeViewHolder { ... - @Override - public View createNodeView(TreeNode node, IconTreeItem value) { - final LayoutInflater inflater = LayoutInflater.from(context); - final View view = inflater.inflate(R.layout.layout_profile_node, null, false); - TextView tvValue = (TextView) view.findViewById(R.id.node_value); - tvValue.setText(value.text); + override fun createNodeView(val node, val value) : View?{ + val inflater = LayoutInflater?.from(context) + val view = inflater?.inflate(R.layout.layout_profile_node, container, false) + val tvValue = view?.findViewById(R.id.node_value) as? TextView + tvValue.setText(value.text) - return view; + return view } ... - public static class IconTreeItem { - public int icon; - public String text; + class IconTreeItem { + val icon + val text } } ``` **5)** Connect view holder with node -```java - IconTreeItem nodeItem = new IconTreeItem(); - TreeNode child1 = new TreeNode(nodeItem).setViewHolder(new MyHolder(mContext)); +```Kotlin + val nodeItem = IconTreeItem() + val child1 = TreeNode(nodeItem).setViewHolder(new MyHolder(mContext)) ``` **6)** Consider using -```java -TreeNode.setClickListener(TreeNodeClickListener listener); +```Kotlin +TreeNode.setClickListener(TreeNodeClickListener listener) AndroidTreeView.setDefaultViewHolder AndroidTreeView.setDefaultNodeClickListener ... diff --git a/gradle.properties b/gradle.properties index d467ea5..3bf73e3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,8 +17,8 @@ # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true -VERSION_NAME=1.2.9 -VERSION_CODE=11 +VERSION_NAME=1.3.0 +VERSION_CODE=12 ANDROID_BUILD_MIN_SDK_VERSION=11