Repository: evrencoskun/TableView
Branch: master
Commit: cf9c7f66e764
Files: 144
Total size: 527.6 KB
Directory structure:
gitextract_wgk_y394/
├── .github/
│ ├── CONTRIBUTING.md
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ ├── config.yml
│ │ └── feature_request.md
│ └── workflows/
│ └── validate_tableview.yml
├── .gitignore
├── LICENSE
├── README.md
├── app/
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ ├── java/
│ │ └── com/
│ │ └── evrencoskun/
│ │ └── tableviewsample/
│ │ ├── MainActivity.java
│ │ ├── MainFragment.java
│ │ └── tableview/
│ │ ├── TableViewAdapter.java
│ │ ├── TableViewListener.java
│ │ ├── TableViewModel.java
│ │ ├── holder/
│ │ │ ├── CellViewHolder.java
│ │ │ ├── ColumnHeaderViewHolder.java
│ │ │ ├── GenderCellViewHolder.java
│ │ │ ├── MoodCellViewHolder.java
│ │ │ └── RowHeaderViewHolder.java
│ │ ├── model/
│ │ │ ├── Cell.java
│ │ │ ├── ColumnHeader.java
│ │ │ └── RowHeader.java
│ │ └── popup/
│ │ ├── ColumnHeaderLongPressPopup.java
│ │ └── RowHeaderLongPressPopup.java
│ └── res/
│ ├── drawable/
│ │ ├── ic_down.xml
│ │ ├── ic_female.xml
│ │ ├── ic_happy.xml
│ │ ├── ic_male.xml
│ │ ├── ic_next.xml
│ │ ├── ic_previous.xml
│ │ ├── ic_sad.xml
│ │ └── ic_up.xml
│ ├── layout/
│ │ ├── activity_main.xml
│ │ ├── fragment_main.xml
│ │ ├── table_view_cell_layout.xml
│ │ ├── table_view_column_header_layout.xml
│ │ ├── table_view_corner_layout.xml
│ │ ├── table_view_image_cell_layout.xml
│ │ └── table_view_row_header_layout.xml
│ └── values/
│ ├── colors.xml
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
├── build.gradle
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── tableview/
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src/
├── androidTest/
│ ├── AndroidManifest.xml
│ ├── java/
│ │ └── com/
│ │ └── evrencoskun/
│ │ └── tableview/
│ │ └── test/
│ │ ├── CornerLayoutTest.java
│ │ ├── CornerViewTest.java
│ │ ├── ReverseLayoutTest.java
│ │ ├── SimpleActivityTest.java
│ │ ├── TestActivity.java
│ │ ├── adapters/
│ │ │ ├── AbstractTableAdapterTest.java
│ │ │ ├── CornerTestAdapter.java
│ │ │ └── SimpleTestAdapter.java
│ │ ├── data/
│ │ │ └── SimpleData.java
│ │ ├── matchers/
│ │ │ └── ViewWidthMatcher.java
│ │ └── models/
│ │ ├── Cell.java
│ │ ├── ColumnHeader.java
│ │ └── RowHeader.java
│ └── res/
│ ├── layout/
│ │ ├── cell_layout.xml
│ │ ├── column_layout.xml
│ │ ├── corner_bottom_left.xml
│ │ ├── corner_bottom_right.xml
│ │ ├── corner_default.xml
│ │ ├── corner_layout.xml
│ │ ├── corner_top_left.xml
│ │ ├── corner_top_right.xml
│ │ ├── reverse_layout.xml
│ │ └── row_layout.xml
│ └── values/
│ ├── colors.xml
│ └── dimens.xml
└── main/
├── AndroidManifest.xml
├── java/
│ └── com/
│ └── evrencoskun/
│ └── tableview/
│ ├── ITableView.java
│ ├── TableView.java
│ ├── adapter/
│ │ ├── AbstractTableAdapter.java
│ │ ├── AdapterDataSetChangedListener.java
│ │ ├── ITableAdapter.java
│ │ └── recyclerview/
│ │ ├── AbstractRecyclerViewAdapter.java
│ │ ├── CellRecyclerView.java
│ │ ├── CellRecyclerViewAdapter.java
│ │ ├── CellRowRecyclerViewAdapter.java
│ │ ├── ColumnHeaderRecyclerViewAdapter.java
│ │ ├── RowHeaderRecyclerViewAdapter.java
│ │ └── holder/
│ │ ├── AbstractSorterViewHolder.java
│ │ └── AbstractViewHolder.java
│ ├── filter/
│ │ ├── Filter.java
│ │ ├── FilterChangedListener.java
│ │ ├── FilterItem.java
│ │ ├── FilterType.java
│ │ └── IFilterableModel.java
│ ├── handler/
│ │ ├── ColumnSortHandler.java
│ │ ├── ColumnWidthHandler.java
│ │ ├── FilterHandler.java
│ │ ├── PreferencesHandler.java
│ │ ├── ScrollHandler.java
│ │ ├── SelectionHandler.java
│ │ └── VisibilityHandler.java
│ ├── layoutmanager/
│ │ ├── CellLayoutManager.java
│ │ ├── ColumnHeaderLayoutManager.java
│ │ └── ColumnLayoutManager.java
│ ├── listener/
│ │ ├── ITableViewListener.java
│ │ ├── SimpleTableViewListener.java
│ │ ├── TableViewLayoutChangeListener.java
│ │ ├── itemclick/
│ │ │ ├── AbstractItemClickListener.java
│ │ │ ├── CellRecyclerViewItemClickListener.java
│ │ │ ├── ColumnHeaderRecyclerViewItemClickListener.java
│ │ │ └── RowHeaderRecyclerViewItemClickListener.java
│ │ └── scroll/
│ │ ├── HorizontalRecyclerViewListener.java
│ │ └── VerticalRecyclerViewListener.java
│ ├── pagination/
│ │ ├── IPagination.java
│ │ └── Pagination.java
│ ├── preference/
│ │ ├── Preferences.java
│ │ └── SavedState.java
│ ├── sort/
│ │ ├── AbstractSortComparator.java
│ │ ├── ColumnForRowHeaderSortComparator.java
│ │ ├── ColumnSortCallback.java
│ │ ├── ColumnSortComparator.java
│ │ ├── ColumnSortHelper.java
│ │ ├── ColumnSortStateChangedListener.java
│ │ ├── ISortableModel.java
│ │ ├── RowHeaderForCellSortComparator.java
│ │ ├── RowHeaderSortCallback.java
│ │ ├── RowHeaderSortComparator.java
│ │ ├── RowHeaderSortHelper.java
│ │ └── SortState.java
│ └── util/
│ └── TableViewUtils.java
└── res/
├── drawable/
│ └── cell_line_divider.xml
└── values/
├── attrs.xml
├── colors.xml
├── dimens.xml
├── ids.xml
└── integers.xml
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/CONTRIBUTING.md
================================================
# Contributing to `TableView`
## Table of contents
- [I need help/I have a question](#i-need-helpi-have-a-question)
- [Reporting a bug](#reporting-a-bug)
- [Suggest a new feature](#suggest-a-new-feature)
- [Contributing to the project](#contributing-to-the-project)
## I need help/I have a question
If you need help to integrate this library in your project, or if you have a generic question about it, please use [Stack Overflow](https://stackoverflow.com/questions/tagged/tableview+android), with the `TableView` and `Android` tags.
## Reporting a bug
If the library is not behaving as you would expect, please [open a new issue](https://github.com/evrencoskun/TableView/issues/new?labels=bug&template=bug_report.md), after checking that it hasn't been reported yet.
Don't forget to provide as much information as you can:
- What are you trying to do?
- What happened instead?
- In case of crash, include the stacktace.
- Which version of the library are you using?
- Which version of Android is this issue happening on?
## Suggest a new feature
If you would like a new feature to be added to the library, please [open a new issue](https://github.com/evrencoskun/TableView/issues/new?labels=enhancement&template=feature_request.md) to describe it.
## Contributing to the project
Every contribution is welcome to the library. Simply [open a new Pull Request](https://github.com/evrencoskun/TableView/compare) with your changes, so they can be reviewed and merged into the project, and eventually released to everyone.
Note that by making a contribution to this project you are agreeing to have your contributions governed by the [MIT License](https://github.com/evrencoskun/TableView/blob/master/LICENSE) copyright statement.
This means that to the extent possible under law, you transfer all copyright and related or neighbouring rights of the code or documents you contribute to the project itself. You also represent that you have the authority to perform the above waiver with respect to the entirety of you contributions.
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: evrencoskun # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
custom: ['https://www.paypal.me/evrencoshkun']
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Stacktrace**
If applicable, add the stacktrace you encountered.
**Tools:**
- `TableView` version: [e.g. 0.8.9.2]
- Android version: [e.g. 11]
**Additional context**
Add any other context about the problem here.
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
contact_links:
- name: Stack Overflow
url: https://stackoverflow.com/questions/tagged/tableview+android
about: Please ask your questions here, with the `TableView` and `Android` tags
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
================================================
FILE: .github/workflows/validate_tableview.yml
================================================
name: Validate TableView
on:
push:
pull_request:
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Gradle Wrapper Validation
uses: gradle/wrapper-validation-action@v1
- name: Setup JDK 1.8
uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Setup Cache
uses: actions/cache@v2
with:
path: |
~/.gradle/caches/
~/.gradle/wrapper/
key: cache-gradle-${{ hashFiles('**/*.gradle') }}
restore-keys: cache-gradle-
- name: Build
run: ./gradlew :tableview:assembleDebug --no-daemon
validate-sample:
name: Validate sample app
needs: build
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup JDK 1.8
uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Setup Cache
uses: actions/cache@v2
with:
path: |
~/.gradle/caches/
~/.gradle/wrapper/
key: cache-gradle-${{ hashFiles('**/*.gradle') }}
restore-keys: cache-gradle-
- name: Build
run: ./gradlew :app:assembleDebug --no-daemon
android-tests:
name: Android Tests
needs: build
# Using macos-latest to take advantage of the hardware acceleration
runs-on: macos-latest
strategy:
matrix:
api-level: [ 15, 29 ]
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup JDK 1.8
uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Setup Cache
uses: actions/cache@v2
with:
path: |
~/.gradle/caches/
~/.gradle/wrapper/
key: cache-gradle-${{ hashFiles('**/*.gradle') }}
restore-keys: cache-gradle-
- name: Android Test - API ${{ matrix.api-level }}
uses: ReactiveCircus/android-emulator-runner@v2
with:
api-level: ${{ matrix.api-level }}
script: ./gradlew :tableview:connectedDebugAndroidTest --no-daemon
================================================
FILE: .gitignore
================================================
# Built application files
*.apk
*.ap_
# Mac
.DS_Store
# Files for the ART/Dalvik VM
*.dex
# Java class files
*.class
# Generated files
bin/
gen/
out/
# Gradle files
.gradle/
build/
# Local configuration file (sdk path, etc)
local.properties
# Proguard folder generated by Eclipse
proguard/
# Log Files
*.log
# Android Studio Navigation editor temp files
.navigation/
# Android Studio captures folder
captures/
# Intellij
*.iml
*.idea/
# Keystore files
*.jks
# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild
# Google Services (e.g. APIs or Firebase)
google-services.json
# Freeline
freeline.py
freeline/
freeline_project_description.json
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2017 Evren Coşkun
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
TableView for Android
TableView is a powerful Android library for displaying complex data structures and rendering tabular data composed of rows, columns and cells.
TableView relies on a separate model object to hold and represent the data it displays. This repository also contains a sample app that is
designed to show you how to create your own TableView in your application.
## Features
- [x] Each column width value can be calculated automatically considering the largest one.
- [x] Setting your own model class to be displayed in a table view easily.
- [x] `TableView` has an action listener interface to listen user touch interaction for each cell.
- [x] `TableView` columns can be sorted in ascending or descending order.
- [x] Hiding & showing the rows and columns is pretty easy.
- [x] Filtering by more than one data.
- [x] Pagination functionality.
## What's new
You can check new implementations of `TableView` on the [release page](https://github.com/evrencoskun/TableView/releases).
## Table of Contents
- [Installation](#installation)
- [Documentation](#documentation)
- [Sample Apps](#sample-apps)
- [Donations](#donations)
- [Contributors](#contributors)
- [License](#license)
## Installation
To use this library in your Android project, just add the following dependency into your module's `build.gradle`:
***Use Jitpack implementation***
1. Check Jitpack use :
```
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
```
2. Add implementation in project build :
```
implementation 'com.github.evrencoskun:TableView:v0.8.9.4'
```
## Documentation
Please check out the [project's wiki](https://github.com/evrencoskun/TableView/wiki).
## Sample Apps
- This repository has a [sample application](https://github.com/evrencoskun/TableView/tree/master/app) of `TableView`.
- [TableViewSample 2](https://github.com/evrencoskun/TableViewSample2)
- [Xently-UI](https://github.com/ajharry69/Xently-UI)
- [Price List Lite](https://pricelistlite.isolpro.in)
- [Database Client for MySQL and PostgreSQL](https://play.google.com/store/apps/details?id=dev.dhruv.databaseclient)
- ([Submit a Pull Request](https://github.com/evrencoskun/TableView/compare) to mention your app on this page)
## Donations
**This project needs you!** If you would like to support this project's further development, the creator of this project or the continuous maintenance of this project, **feel free to donate**. Your donation is highly appreciated (and I love food, coffee and beer). Thank you!
**PayPal**
- [**Donate 5 $**](https://www.paypal.me/evrencoshkun): Thank's for creating this project, here's a coffee (or some beer) for you!
- [**Donate 10 $**](https://www.paypal.me/evrencoshkun): Wow, I am stunned. Let me take you to the movies!
- [**Donate 15 $**](https://www.paypal.me/evrencoshkun): I really appreciate your work, let's grab some lunch!
- [**Donate 25 $**](https://www.paypal.me/evrencoshkun): That's some awesome stuff you did right there, dinner is on me!
- Or you can also [**choose what you want to donate**](https://www.paypal.me/evrencoshkun), all donations are awesome!
## Contributors
Contributions of any kind are welcome! I would like to thank all the [contributors](https://github.com/evrencoskun/TableView/graphs/contributors) for sharing code and
making `TableView` a better product.
If you wish to contribute to this project, please refer to our [contributing guide](.github/CONTRIBUTING.md).
## License
```
MIT License
Copyright (c) 2021 Evren Coşkun
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```
================================================
FILE: app/.gitignore
================================================
/build
================================================
FILE: app/build.gradle
================================================
/*
* MIT License
*
* Copyright (c) 2021 Evren Coşkun
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
apply plugin: 'com.android.application'
android {
compileSdkVersion compile_sdk_version
defaultConfig {
applicationId 'com.evrencoskun.tableviewsample'
minSdkVersion min_sdk_version
targetSdkVersion target_sdk_version
versionCode 1
versionName '1.0'
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
vectorDrawables.useSupportLibrary = true
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility = java_version
targetCompatibility = java_version
}
buildFeatures {
buildConfig = false
}
lintOptions {
abortOnError false
}
}
dependencies {
implementation project(path: ':tableview')
implementation "androidx.annotation:annotation:$androidx_annotation_version"
implementation "androidx.appcompat:appcompat:$androidx_appcompat_version"
implementation "androidx.fragment:fragment:$androidx_fragment_version"
implementation "androidx.recyclerview:recyclerview:$androidx_recyclerview_version"
}
================================================
FILE: app/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/evrencoskun/Library/Android/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
================================================
FILE: app/src/main/AndroidManifest.xml
================================================
================================================
FILE: app/src/main/java/com/evrencoskun/tableviewsample/MainActivity.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Evren Coşkun
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableviewsample;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
public MainActivity() {
super(R.layout.activity_main);
}
}
================================================
FILE: app/src/main/java/com/evrencoskun/tableviewsample/MainFragment.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Evren Coşkun
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableviewsample;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.view.View;
import android.widget.AdapterView;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.Spinner;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.evrencoskun.tableview.TableView;
import com.evrencoskun.tableview.filter.Filter;
import com.evrencoskun.tableview.pagination.Pagination;
import com.evrencoskun.tableviewsample.tableview.TableViewAdapter;
import com.evrencoskun.tableviewsample.tableview.TableViewListener;
import com.evrencoskun.tableviewsample.tableview.TableViewModel;
/**
* A simple {@link Fragment} subclass.
*/
public class MainFragment extends Fragment {
private Spinner moodFilter, genderFilter;
private ImageButton previousButton, nextButton;
private TextView tablePaginationDetails;
private TableView mTableView;
@Nullable
private Filter mTableFilter; // This is used for filtering the table.
@Nullable
private Pagination mPagination; // This is used for paginating the table.
private boolean mPaginationEnabled = false;
public MainFragment() {
super(R.layout.fragment_main);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
EditText searchField = view.findViewById(R.id.query_string);
searchField.addTextChangedListener(mSearchTextWatcher);
moodFilter = view.findViewById(R.id.mood_spinner);
moodFilter.setOnItemSelectedListener(mItemSelectionListener);
genderFilter = view.findViewById(R.id.gender_spinner);
genderFilter.setOnItemSelectedListener(mItemSelectionListener);
Spinner itemsPerPage = view.findViewById(R.id.items_per_page_spinner);
View tableTestContainer = view.findViewById(R.id.table_test_container);
previousButton = view.findViewById(R.id.previous_button);
nextButton = view.findViewById(R.id.next_button);
EditText pageNumberField = view.findViewById(R.id.page_number_text);
tablePaginationDetails = view.findViewById(R.id.table_details);
if (mPaginationEnabled) {
tableTestContainer.setVisibility(View.VISIBLE);
itemsPerPage.setOnItemSelectedListener(onItemsPerPageSelectedListener);
previousButton.setOnClickListener(mClickListener);
nextButton.setOnClickListener(mClickListener);
pageNumberField.addTextChangedListener(onPageTextChanged);
} else {
tableTestContainer.setVisibility(View.GONE);
}
// Let's get TableView
mTableView = view.findViewById(R.id.tableview);
initializeTableView();
if (mPaginationEnabled) {
mTableFilter = new Filter(mTableView); // Create an instance of a Filter and pass the
// created TableView.
// Create an instance for the TableView pagination and pass the created TableView.
mPagination = new Pagination(mTableView);
// Sets the pagination listener of the TableView pagination to handle
// pagination actions. See onTableViewPageTurnedListener variable declaration below.
mPagination.setOnTableViewPageTurnedListener(onTableViewPageTurnedListener);
}
}
private void initializeTableView() {
// Create TableView View model class to group view models of TableView
TableViewModel tableViewModel = new TableViewModel();
// Create TableView Adapter
TableViewAdapter tableViewAdapter = new TableViewAdapter(tableViewModel);
mTableView.setAdapter(tableViewAdapter);
mTableView.setTableViewListener(new TableViewListener(mTableView));
// Create an instance of a Filter and pass the TableView.
//mTableFilter = new Filter(mTableView);
// Load the dummy data to the TableView
tableViewAdapter.setAllItems(tableViewModel.getColumnHeaderList(), tableViewModel
.getRowHeaderList(), tableViewModel.getCellList());
//mTableView.setHasFixedWidth(true);
/*for (int i = 0; i < mTableViewModel.getCellList().size(); i++) {
mTableView.setColumnWidth(i, 200);
}*)
//mTableView.setColumnWidth(0, -2);
//mTableView.setColumnWidth(1, -2);
/*mTableView.setColumnWidth(2, 200);
mTableView.setColumnWidth(3, 300);
mTableView.setColumnWidth(4, 400);
mTableView.setColumnWidth(5, 500);*/
}
public void filterTable(@NonNull String filter) {
// Sets a filter to the table, this will filter ALL the columns.
if (mTableFilter != null) {
mTableFilter.set(filter);
}
}
public void filterTableForMood(@NonNull String filter) {
// Sets a filter to the table, this will only filter a specific column.
// In the example data, this will filter the mood column.
if (mTableFilter != null) {
mTableFilter.set(TableViewModel.MOOD_COLUMN_INDEX, filter);
}
}
public void filterTableForGender(@NonNull String filter) {
// Sets a filter to the table, this will only filter a specific column.
// In the example data, this will filter the gender column.
if (mTableFilter != null) {
mTableFilter.set(TableViewModel.GENDER_COLUMN_INDEX, filter);
}
}
// The following four methods below: nextTablePage(), previousTablePage(),
// goToTablePage(int page) and setTableItemsPerPage(int itemsPerPage)
// are for controlling the TableView pagination.
public void nextTablePage() {
if (mPagination != null) {
mPagination.nextPage();
}
}
public void previousTablePage() {
if (mPagination != null) {
mPagination.previousPage();
}
}
public void goToTablePage(int page) {
if (mPagination != null) {
mPagination.goToPage(page);
}
}
public void setTableItemsPerPage(int itemsPerPage) {
if (mPagination != null) {
mPagination.setItemsPerPage(itemsPerPage);
}
}
// Handler for the changing of pages in the paginated TableView.
@NonNull
private final Pagination.OnTableViewPageTurnedListener onTableViewPageTurnedListener = new
Pagination.OnTableViewPageTurnedListener() {
@Override
public void onPageTurned(int numItems, int itemsStart, int itemsEnd) {
int currentPage = mPagination.getCurrentPage();
int pageCount = mPagination.getPageCount();
previousButton.setVisibility(View.VISIBLE);
nextButton.setVisibility(View.VISIBLE);
if (currentPage == 1 && pageCount == 1) {
previousButton.setVisibility(View.INVISIBLE);
nextButton.setVisibility(View.INVISIBLE);
}
if (currentPage == 1) {
previousButton.setVisibility(View.INVISIBLE);
}
if (currentPage == pageCount) {
nextButton.setVisibility(View.INVISIBLE);
}
tablePaginationDetails.setText(getString(R.string.table_pagination_details, String
.valueOf(currentPage), String.valueOf(itemsStart), String.valueOf(itemsEnd)));
}
};
@NonNull
private final AdapterView.OnItemSelectedListener mItemSelectionListener = new AdapterView
.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView> parent, View view, int position, long id) {
// 0. index is for empty item of spinner.
if (position > 0) {
String filter = Integer.toString(position);
if (parent == moodFilter) {
filterTableForMood(filter);
} else if (parent == genderFilter) {
filterTableForGender(filter);
}
}
}
@Override
public void onNothingSelected(AdapterView> parent) {
// Left empty intentionally.
}
};
@NonNull
private final TextWatcher mSearchTextWatcher = new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
filterTable(String.valueOf(s));
}
@Override
public void afterTextChanged(Editable s) {
}
};
@NonNull
private final AdapterView.OnItemSelectedListener onItemsPerPageSelectedListener = new AdapterView
.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView> parent, View view, int position, long id) {
int itemsPerPage;
if ("All".equals(parent.getItemAtPosition(position).toString())) {
itemsPerPage = 0;
} else {
itemsPerPage = Integer.parseInt(parent.getItemAtPosition(position).toString());
}
setTableItemsPerPage(itemsPerPage);
}
@Override
public void onNothingSelected(AdapterView> parent) {
}
};
@NonNull
private final View.OnClickListener mClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
if (v == previousButton) {
previousTablePage();
} else if (v == nextButton) {
nextTablePage();
}
}
};
@NonNull
private final TextWatcher onPageTextChanged = new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
int page;
if (TextUtils.isEmpty(s)) {
page = 1;
} else {
page = Integer.parseInt(String.valueOf(s));
}
goToTablePage(page);
}
@Override
public void afterTextChanged(Editable s) {
}
};
}
================================================
FILE: app/src/main/java/com/evrencoskun/tableviewsample/tableview/TableViewAdapter.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Evren Coşkun
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableviewsample.tableview;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.evrencoskun.tableview.adapter.AbstractTableAdapter;
import com.evrencoskun.tableview.adapter.recyclerview.holder.AbstractViewHolder;
import com.evrencoskun.tableview.sort.SortState;
import com.evrencoskun.tableviewsample.R;
import com.evrencoskun.tableviewsample.tableview.holder.CellViewHolder;
import com.evrencoskun.tableviewsample.tableview.holder.ColumnHeaderViewHolder;
import com.evrencoskun.tableviewsample.tableview.holder.GenderCellViewHolder;
import com.evrencoskun.tableviewsample.tableview.holder.MoodCellViewHolder;
import com.evrencoskun.tableviewsample.tableview.holder.RowHeaderViewHolder;
import com.evrencoskun.tableviewsample.tableview.model.Cell;
import com.evrencoskun.tableviewsample.tableview.model.ColumnHeader;
import com.evrencoskun.tableviewsample.tableview.model.RowHeader;
/**
* Created by evrencoskun on 11/06/2017.
*
* This is a sample of custom TableView Adapter.
*/
public class TableViewAdapter extends AbstractTableAdapter {
// Cell View Types by Column Position
private static final int MOOD_CELL_TYPE = 1;
private static final int GENDER_CELL_TYPE = 2;
// add new one if it necessary..
private static final String LOG_TAG = TableViewAdapter.class.getSimpleName();
@NonNull
private final TableViewModel mTableViewModel;
public TableViewAdapter(@NonNull TableViewModel tableViewModel) {
super();
this.mTableViewModel = tableViewModel;
}
/**
* This is where you create your custom Cell ViewHolder. This method is called when Cell
* RecyclerView of the TableView needs a new RecyclerView.ViewHolder of the given type to
* represent an item.
*
* @param viewType : This value comes from "getCellItemViewType" method to support different
* type of viewHolder as a Cell item.
* @see #getCellItemViewType(int);
*/
@NonNull
@Override
public AbstractViewHolder onCreateCellViewHolder(@NonNull ViewGroup parent, int viewType) {
//TODO check
Log.e(LOG_TAG, " onCreateCellViewHolder has been called");
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View layout;
switch (viewType) {
case MOOD_CELL_TYPE:
// Get image cell layout which has ImageView on the base instead of TextView.
layout = inflater.inflate(R.layout.table_view_image_cell_layout, parent, false);
return new MoodCellViewHolder(layout);
case GENDER_CELL_TYPE:
// Get image cell layout which has ImageView instead of TextView.
layout = inflater.inflate(R.layout.table_view_image_cell_layout, parent, false);
return new GenderCellViewHolder(layout);
default:
// For cells that display a text
layout = inflater.inflate(R.layout.table_view_cell_layout, parent, false);
// Create a Cell ViewHolder
return new CellViewHolder(layout);
}
}
/**
* That is where you set Cell View Model data to your custom Cell ViewHolder. This method is
* Called by Cell RecyclerView of the TableView to display the data at the specified position.
* This method gives you everything you need about a cell item.
*
* @param holder : This is one of your cell ViewHolders that was created on
* ```onCreateCellViewHolder``` method. In this example we have created
* "CellViewHolder" holder.
* @param cellItemModel : This is the cell view model located on this X and Y position. In this
* example, the model class is "Cell".
* @param columnPosition : This is the X (Column) position of the cell item.
* @param rowPosition : This is the Y (Row) position of the cell item.
* @see #onCreateCellViewHolder(ViewGroup, int) ;
*/
@Override
public void onBindCellViewHolder(@NonNull AbstractViewHolder holder, @Nullable Cell cellItemModel, int
columnPosition, int rowPosition) {
switch (holder.getItemViewType()) {
case MOOD_CELL_TYPE:
MoodCellViewHolder moodViewHolder = (MoodCellViewHolder) holder;
moodViewHolder.cell_image.setImageResource(mTableViewModel.getDrawable((int) cellItemModel
.getData(), false));
break;
case GENDER_CELL_TYPE:
GenderCellViewHolder genderViewHolder = (GenderCellViewHolder) holder;
genderViewHolder.cell_image.setImageResource(mTableViewModel.getDrawable((int)
cellItemModel.getData(), true));
break;
default:
// Get the holder to update cell item text
CellViewHolder viewHolder = (CellViewHolder) holder;
viewHolder.setCell(cellItemModel);
break;
}
}
/**
* This is where you create your custom Column Header ViewHolder. This method is called when
* Column Header RecyclerView of the TableView needs a new RecyclerView.ViewHolder of the given
* type to represent an item.
*
* @param viewType : This value comes from "getColumnHeaderItemViewType" method to support
* different type of viewHolder as a Column Header item.
* @see #getColumnHeaderItemViewType(int);
*/
@NonNull
@Override
public AbstractViewHolder onCreateColumnHeaderViewHolder(@NonNull ViewGroup parent, int viewType) {
// TODO: check
//Log.e(LOG_TAG, " onCreateColumnHeaderViewHolder has been called");
// Get Column Header xml Layout
View layout = LayoutInflater.from(parent.getContext())
.inflate(R.layout.table_view_column_header_layout, parent, false);
// Create a ColumnHeader ViewHolder
return new ColumnHeaderViewHolder(layout, getTableView());
}
/**
* That is where you set Column Header View Model data to your custom Column Header ViewHolder.
* This method is Called by ColumnHeader RecyclerView of the TableView to display the data at
* the specified position. This method gives you everything you need about a column header
* item.
*
* @param holder : This is one of your column header ViewHolders that was created
* on ```onCreateColumnHeaderViewHolder``` method. In this example
* we have created "ColumnHeaderViewHolder" holder.
* @param columnHeaderItemModel : This is the column header view model located on this X
* position. In this example, the model class is "ColumnHeader".
* @param columnPosition : This is the X (Column) position of the column header item.
* @see #onCreateColumnHeaderViewHolder(ViewGroup, int) ;
*/
@Override
public void onBindColumnHeaderViewHolder(@NonNull AbstractViewHolder holder, @Nullable ColumnHeader
columnHeaderItemModel, int columnPosition) {
// Get the holder to update cell item text
ColumnHeaderViewHolder columnHeaderViewHolder = (ColumnHeaderViewHolder) holder;
columnHeaderViewHolder.setColumnHeader(columnHeaderItemModel);
}
/**
* This is where you create your custom Row Header ViewHolder. This method is called when
* Row Header RecyclerView of the TableView needs a new RecyclerView.ViewHolder of the given
* type to represent an item.
*
* @param viewType : This value comes from "getRowHeaderItemViewType" method to support
* different type of viewHolder as a row Header item.
* @see #getRowHeaderItemViewType(int);
*/
@NonNull
@Override
public AbstractViewHolder onCreateRowHeaderViewHolder(@NonNull ViewGroup parent, int viewType) {
// Get Row Header xml Layout
View layout = LayoutInflater.from(parent.getContext())
.inflate(R.layout.table_view_row_header_layout, parent, false);
// Create a Row Header ViewHolder
return new RowHeaderViewHolder(layout);
}
/**
* That is where you set Row Header View Model data to your custom Row Header ViewHolder. This
* method is Called by RowHeader RecyclerView of the TableView to display the data at the
* specified position. This method gives you everything you need about a row header item.
*
* @param holder : This is one of your row header ViewHolders that was created on
* ```onCreateRowHeaderViewHolder``` method. In this example we have
* created "RowHeaderViewHolder" holder.
* @param rowHeaderItemModel : This is the row header view model located on this Y position. In
* this example, the model class is "RowHeader".
* @param rowPosition : This is the Y (row) position of the row header item.
* @see #onCreateRowHeaderViewHolder(ViewGroup, int) ;
*/
@Override
public void onBindRowHeaderViewHolder(@NonNull AbstractViewHolder holder, @Nullable RowHeader rowHeaderItemModel,
int rowPosition) {
// Get the holder to update row header item text
RowHeaderViewHolder rowHeaderViewHolder = (RowHeaderViewHolder) holder;
rowHeaderViewHolder.row_header_textview.setText(String.valueOf(rowHeaderItemModel.getData()));
}
@NonNull
@Override
public View onCreateCornerView(@NonNull ViewGroup parent) {
// Get Corner xml layout
View corner = LayoutInflater.from(parent.getContext())
.inflate(R.layout.table_view_corner_layout, parent, false);
corner.setOnClickListener(view -> {
SortState sortState = TableViewAdapter.this.getTableView()
.getRowHeaderSortingStatus();
if (sortState != SortState.ASCENDING) {
Log.d("TableViewAdapter", "Order Ascending");
TableViewAdapter.this.getTableView().sortRowHeader(SortState.ASCENDING);
} else {
Log.d("TableViewAdapter", "Order Descending");
TableViewAdapter.this.getTableView().sortRowHeader(SortState.DESCENDING);
}
});
return corner;
}
@Override
public int getColumnHeaderItemViewType(int position) {
// The unique ID for this type of column header item
// If you have different items for Cell View by X (Column) position,
// then you should fill this method to be able create different
// type of CellViewHolder on "onCreateCellViewHolder"
return 0;
}
@Override
public int getRowHeaderItemViewType(int position) {
// The unique ID for this type of row header item
// If you have different items for Row Header View by Y (Row) position,
// then you should fill this method to be able create different
// type of RowHeaderViewHolder on "onCreateRowHeaderViewHolder"
return 0;
}
@Override
public int getCellItemViewType(int column) {
// The unique ID for this type of cell item
// If you have different items for Cell View by X (Column) position,
// then you should fill this method to be able create different
// type of CellViewHolder on "onCreateCellViewHolder"
switch (column) {
case TableViewModel.MOOD_COLUMN_INDEX:
return MOOD_CELL_TYPE;
case TableViewModel.GENDER_COLUMN_INDEX:
return GENDER_CELL_TYPE;
default:
// Default view type
return 0;
}
}
}
================================================
FILE: app/src/main/java/com/evrencoskun/tableviewsample/tableview/TableViewListener.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Evren Coşkun
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableviewsample.tableview;
import android.content.Context;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.evrencoskun.tableview.TableView;
import com.evrencoskun.tableview.listener.ITableViewListener;
import com.evrencoskun.tableviewsample.tableview.holder.ColumnHeaderViewHolder;
import com.evrencoskun.tableviewsample.tableview.popup.ColumnHeaderLongPressPopup;
import com.evrencoskun.tableviewsample.tableview.popup.RowHeaderLongPressPopup;
/**
* Created by evrencoskun on 21/09/2017.
*/
public class TableViewListener implements ITableViewListener {
@NonNull
private final Context mContext;
@NonNull
private final TableView mTableView;
public TableViewListener(@NonNull TableView tableView) {
this.mContext = tableView.getContext();
this.mTableView = tableView;
}
/**
* Called when user click any cell item.
*
* @param cellView : Clicked Cell ViewHolder.
* @param column : X (Column) position of Clicked Cell item.
* @param row : Y (Row) position of Clicked Cell item.
*/
@Override
public void onCellClicked(@NonNull RecyclerView.ViewHolder cellView, int column, int row) {
// Do what you want.
showToast("Cell " + column + " " + row + " has been clicked.");
}
/**
* Called when user double click any cell item.
*
* @param cellView : Clicked Cell ViewHolder.
* @param column : X (Column) position of Clicked Cell item.
* @param row : Y (Row) position of Clicked Cell item.
*/
@Override
public void onCellDoubleClicked(@NonNull RecyclerView.ViewHolder cellView, int column, int row) {
// Do what you want.
showToast("Cell " + column + " " + row + " has been double clicked.");
}
/**
* Called when user long press any cell item.
*
* @param cellView : Long Pressed Cell ViewHolder.
* @param column : X (Column) position of Long Pressed Cell item.
* @param row : Y (Row) position of Long Pressed Cell item.
*/
@Override
public void onCellLongPressed(@NonNull RecyclerView.ViewHolder cellView, final int column,
int row) {
// Do What you want
showToast("Cell " + column + " " + row + " has been long pressed.");
}
/**
* Called when user click any column header item.
*
* @param columnHeaderView : Clicked Column Header ViewHolder.
* @param column : X (Column) position of Clicked Column Header item.
*/
@Override
public void onColumnHeaderClicked(@NonNull RecyclerView.ViewHolder columnHeaderView, int
column) {
// Do what you want.
showToast("Column header " + column + " has been clicked.");
}
/**
* Called when user double click any column header item.
*
* @param columnHeaderView : Clicked Column Header ViewHolder.
* @param column : X (Column) position of Clicked Column Header item.
*/
@Override
public void onColumnHeaderDoubleClicked(@NonNull RecyclerView.ViewHolder columnHeaderView, int column) {
// Do what you want.
showToast("Column header " + column + " has been double clicked.");
}
/**
* Called when user long press any column header item.
*
* @param columnHeaderView : Long Pressed Column Header ViewHolder.
* @param column : X (Column) position of Long Pressed Column Header item.
*/
@Override
public void onColumnHeaderLongPressed(@NonNull RecyclerView.ViewHolder columnHeaderView, int
column) {
if (columnHeaderView instanceof ColumnHeaderViewHolder) {
// Create Long Press Popup
ColumnHeaderLongPressPopup popup = new ColumnHeaderLongPressPopup(
(ColumnHeaderViewHolder) columnHeaderView, mTableView);
// Show
popup.show();
}
}
/**
* Called when user click any Row Header item.
*
* @param rowHeaderView : Clicked Row Header ViewHolder.
* @param row : Y (Row) position of Clicked Row Header item.
*/
@Override
public void onRowHeaderClicked(@NonNull RecyclerView.ViewHolder rowHeaderView, int row) {
// Do whatever you want.
showToast("Row header " + row + " has been clicked.");
}
/**
* Called when user double click any Row Header item.
*
* @param rowHeaderView : Clicked Row Header ViewHolder.
* @param row : Y (Row) position of Clicked Row Header item.
*/
@Override
public void onRowHeaderDoubleClicked(@NonNull RecyclerView.ViewHolder rowHeaderView, int row) {
// Do whatever you want.
showToast("Row header " + row + " has been double clicked.");
}
/**
* Called when user long press any row header item.
*
* @param rowHeaderView : Long Pressed Row Header ViewHolder.
* @param row : Y (Row) position of Long Pressed Row Header item.
*/
@Override
public void onRowHeaderLongPressed(@NonNull RecyclerView.ViewHolder rowHeaderView, int row) {
// Create Long Press Popup
RowHeaderLongPressPopup popup = new RowHeaderLongPressPopup(rowHeaderView, mTableView);
// Show
popup.show();
}
private void showToast(String p_strMessage) {
Toast.makeText(mContext, p_strMessage, Toast.LENGTH_SHORT).show();
}
}
================================================
FILE: app/src/main/java/com/evrencoskun/tableviewsample/tableview/TableViewModel.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Evren Coşkun
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableviewsample.tableview;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import com.evrencoskun.tableviewsample.R;
import com.evrencoskun.tableviewsample.tableview.model.Cell;
import com.evrencoskun.tableviewsample.tableview.model.ColumnHeader;
import com.evrencoskun.tableviewsample.tableview.model.RowHeader;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
/**
* Created by evrencoskun on 4.02.2018.
*/
public class TableViewModel {
// Columns indexes
public static final int MOOD_COLUMN_INDEX = 3;
public static final int GENDER_COLUMN_INDEX = 4;
// Constant values for icons
public static final int SAD = 1;
public static final int HAPPY = 2;
public static final int BOY = 1;
public static final int GIRL = 2;
// Constant size for dummy data sets
private static final int COLUMN_SIZE = 500;
private static final int ROW_SIZE = 500;
// Drawables
@DrawableRes
private final int mBoyDrawable;
@DrawableRes
private final int mGirlDrawable;
@DrawableRes
private final int mHappyDrawable;
@DrawableRes
private final int mSadDrawable;
public TableViewModel() {
// initialize drawables
mBoyDrawable = R.drawable.ic_male;
mGirlDrawable = R.drawable.ic_female;
mHappyDrawable = R.drawable.ic_happy;
mSadDrawable = R.drawable.ic_sad;
}
@NonNull
private List getSimpleRowHeaderList() {
List list = new ArrayList<>();
for (int i = 0; i < ROW_SIZE; i++) {
RowHeader header = new RowHeader(String.valueOf(i), "row " + i);
list.add(header);
}
return list;
}
/**
* This is a dummy model list test some cases.
*/
@NonNull
private List getRandomColumnHeaderList() {
List list = new ArrayList<>();
for (int i = 0; i < COLUMN_SIZE; i++) {
String title = "column " + i;
int nRandom = new Random().nextInt();
if (nRandom % 4 == 0 || nRandom % 3 == 0 || nRandom == i) {
title = "large column " + i;
}
ColumnHeader header = new ColumnHeader(String.valueOf(i), title);
list.add(header);
}
return list;
}
/**
* This is a dummy model list test some cases.
*/
@NonNull
private List> getCellListForSortingTest() {
List> list = new ArrayList<>();
for (int i = 0; i < ROW_SIZE; i++) {
List cellList = new ArrayList<>();
for (int j = 0; j < COLUMN_SIZE; j++) {
Object text = "cell " + j + " " + i;
final int random = new Random().nextInt();
if (j == 0) {
text = i;
} else if (j == 1) {
text = random;
} else if (j == MOOD_COLUMN_INDEX) {
text = random % 2 == 0 ? HAPPY : SAD;
} else if (j == GENDER_COLUMN_INDEX) {
text = random % 2 == 0 ? BOY : GIRL;
}
// Create dummy id.
String id = j + "-" + i;
Cell cell;
if (j == 3) {
cell = new Cell(id, text);
} else if (j == 4) {
// NOTE female and male keywords for filter will have conflict since "female"
// contains "male"
cell = new Cell(id, text);
} else {
cell = new Cell(id, text);
}
cellList.add(cell);
}
list.add(cellList);
}
return list;
}
@DrawableRes
public int getDrawable(int value, boolean isGender) {
if (isGender) {
return value == BOY ? mBoyDrawable : mGirlDrawable;
} else {
return value == SAD ? mSadDrawable : mHappyDrawable;
}
}
@NonNull
public List> getCellList() {
return getCellListForSortingTest();
}
@NonNull
public List getRowHeaderList() {
return getSimpleRowHeaderList();
}
@NonNull
public List getColumnHeaderList() {
return getRandomColumnHeaderList();
}
}
================================================
FILE: app/src/main/java/com/evrencoskun/tableviewsample/tableview/holder/CellViewHolder.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Evren Coşkun
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableviewsample.tableview.holder;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.evrencoskun.tableview.adapter.recyclerview.holder.AbstractViewHolder;
import com.evrencoskun.tableviewsample.R;
import com.evrencoskun.tableviewsample.tableview.model.Cell;
/**
* Created by evrencoskun on 23/10/2017.
*/
public class CellViewHolder extends AbstractViewHolder {
@NonNull
private final TextView cell_textview;
@NonNull
private final LinearLayout cell_container;
public CellViewHolder(@NonNull View itemView) {
super(itemView);
cell_textview = itemView.findViewById(R.id.cell_data);
cell_container = itemView.findViewById(R.id.cell_container);
}
public void setCell(@Nullable Cell cell) {
cell_textview.setText(String.valueOf(cell.getData()));
// If your TableView should have auto resize for cells & columns.
// Then you should consider the below lines. Otherwise, you can ignore them.
// It is necessary to remeasure itself.
cell_container.getLayoutParams().width = LinearLayout.LayoutParams.WRAP_CONTENT;
cell_textview.requestLayout();
}
}
================================================
FILE: app/src/main/java/com/evrencoskun/tableviewsample/tableview/holder/ColumnHeaderViewHolder.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Evren Coşkun
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableviewsample.tableview.holder;
import android.util.Log;
import android.view.View;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.evrencoskun.tableview.ITableView;
import com.evrencoskun.tableview.adapter.recyclerview.holder.AbstractSorterViewHolder;
import com.evrencoskun.tableview.sort.SortState;
import com.evrencoskun.tableviewsample.R;
import com.evrencoskun.tableviewsample.tableview.model.ColumnHeader;
/**
* Created by evrencoskun on 23/10/2017.
*/
public class ColumnHeaderViewHolder extends AbstractSorterViewHolder {
private static final String LOG_TAG = ColumnHeaderViewHolder.class.getSimpleName();
@NonNull
private final LinearLayout column_header_container;
@NonNull
private final TextView column_header_textview;
@NonNull
private final ImageButton column_header_sortButton;
@Nullable
private final ITableView tableView;
public ColumnHeaderViewHolder(@NonNull View itemView, @Nullable ITableView tableView) {
super(itemView);
this.tableView = tableView;
column_header_textview = itemView.findViewById(R.id.column_header_textView);
column_header_container = itemView.findViewById(R.id.column_header_container);
column_header_sortButton = itemView.findViewById(R.id.column_header_sortButton);
// Set click listener to the sort button
column_header_sortButton.setOnClickListener(mSortButtonClickListener);
}
/**
* This method is calling from onBindColumnHeaderHolder on TableViewAdapter
*/
public void setColumnHeader(@Nullable ColumnHeader columnHeader) {
column_header_textview.setText(String.valueOf(columnHeader.getData()));
// If your TableView should have auto resize for cells & columns.
// Then you should consider the below lines. Otherwise, you can remove them.
// It is necessary to remeasure itself.
column_header_container.getLayoutParams().width = LinearLayout.LayoutParams.WRAP_CONTENT;
column_header_textview.requestLayout();
}
@NonNull
private final View.OnClickListener mSortButtonClickListener = new View.OnClickListener() {
@Override
public void onClick(View view) {
if (getSortState() == SortState.ASCENDING) {
tableView.sortColumn(getBindingAdapterPosition(), SortState.DESCENDING);
} else if (getSortState() == SortState.DESCENDING) {
tableView.sortColumn(getBindingAdapterPosition(), SortState.ASCENDING);
} else {
// Default one
tableView.sortColumn(getBindingAdapterPosition(), SortState.DESCENDING);
}
}
};
@Override
public void onSortingStatusChanged(@NonNull SortState sortState) {
Log.e(LOG_TAG, " + onSortingStatusChanged: x: " + getBindingAdapterPosition() + ", " +
"old state: " + getSortState() + ", current state: " + sortState + ", " +
"visibility: " + column_header_sortButton.getVisibility());
super.onSortingStatusChanged(sortState);
// It is necessary to remeasure itself.
column_header_container.getLayoutParams().width = LinearLayout.LayoutParams.WRAP_CONTENT;
controlSortState(sortState);
Log.e(LOG_TAG, " - onSortingStatusChanged: x: " + getBindingAdapterPosition() + ", " +
"old state: " + getSortState() + ", current state: " + sortState + ", " +
"visibility: " + column_header_sortButton.getVisibility());
column_header_textview.requestLayout();
column_header_sortButton.requestLayout();
column_header_container.requestLayout();
itemView.requestLayout();
}
private void controlSortState(@NonNull SortState sortState) {
if (sortState == SortState.ASCENDING) {
column_header_sortButton.setVisibility(View.VISIBLE);
column_header_sortButton.setImageResource(R.drawable.ic_down);
} else if (sortState == SortState.DESCENDING) {
column_header_sortButton.setVisibility(View.VISIBLE);
column_header_sortButton.setImageResource(R.drawable.ic_up);
} else {
column_header_sortButton.setVisibility(View.INVISIBLE);
}
}
}
================================================
FILE: app/src/main/java/com/evrencoskun/tableviewsample/tableview/holder/GenderCellViewHolder.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Evren Coşkun
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableviewsample.tableview.holder;
import android.view.View;
import androidx.annotation.NonNull;
import com.evrencoskun.tableviewsample.R;
import com.evrencoskun.tableviewsample.tableview.TableViewModel;
/**
* Created by evrencoskun on 4.02.2018.
*/
public class GenderCellViewHolder extends MoodCellViewHolder {
public GenderCellViewHolder(@NonNull View itemView) {
super(itemView);
}
@Override
public void setData(Object data) {
int gender = (int) data;
int genderDrawable = gender == TableViewModel.BOY ? R.drawable.ic_male : R.drawable.ic_female;
cell_image.setImageResource(genderDrawable);
}
}
================================================
FILE: app/src/main/java/com/evrencoskun/tableviewsample/tableview/holder/MoodCellViewHolder.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Evren Coşkun
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableviewsample.tableview.holder;
import android.view.View;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import com.evrencoskun.tableview.adapter.recyclerview.holder.AbstractViewHolder;
import com.evrencoskun.tableviewsample.R;
import com.evrencoskun.tableviewsample.tableview.TableViewModel;
/**
* Created by evrencoskun on 4.02.2018.
*/
public class MoodCellViewHolder extends AbstractViewHolder {
@NonNull
public final ImageView cell_image;
public MoodCellViewHolder(@NonNull View itemView) {
super(itemView);
cell_image = itemView.findViewById(R.id.cell_image);
}
public void setData(Object data) {
int mood = (int) data;
int moodDrawable = mood == TableViewModel.HAPPY ? R.drawable.ic_happy : R.drawable.ic_sad;
cell_image.setImageResource(moodDrawable);
}
}
================================================
FILE: app/src/main/java/com/evrencoskun/tableviewsample/tableview/holder/RowHeaderViewHolder.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Evren Coşkun
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableviewsample.tableview.holder;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.NonNull;
import com.evrencoskun.tableview.adapter.recyclerview.holder.AbstractViewHolder;
import com.evrencoskun.tableviewsample.R;
/**
* Created by evrencoskun on 23/10/2017.
*/
public class RowHeaderViewHolder extends AbstractViewHolder {
@NonNull
public final TextView row_header_textview;
public RowHeaderViewHolder(@NonNull View itemView) {
super(itemView);
row_header_textview = itemView.findViewById(R.id.row_header_textview);
}
}
================================================
FILE: app/src/main/java/com/evrencoskun/tableviewsample/tableview/model/Cell.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Evren Coşkun
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableviewsample.tableview.model;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.evrencoskun.tableview.filter.IFilterableModel;
import com.evrencoskun.tableview.sort.ISortableModel;
/**
* Created by evrencoskun on 11/06/2017.
*/
public class Cell implements ISortableModel, IFilterableModel {
@NonNull
private final String mId;
@Nullable
private final Object mData;
@NonNull
private final String mFilterKeyword;
public Cell(@NonNull String id, @Nullable Object data) {
this.mId = id;
this.mData = data;
this.mFilterKeyword = String.valueOf(data);
}
/**
* This is necessary for sorting process.
* See ISortableModel
*/
@NonNull
@Override
public String getId() {
return mId;
}
/**
* This is necessary for sorting process.
* See ISortableModel
*/
@Nullable
@Override
public Object getContent() {
return mData;
}
@Nullable
public Object getData() {
return mData;
}
@NonNull
@Override
public String getFilterableKeyword() {
return mFilterKeyword;
}
}
================================================
FILE: app/src/main/java/com/evrencoskun/tableviewsample/tableview/model/ColumnHeader.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Evren Coşkun
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableviewsample.tableview.model;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
/**
* Created by evrencoskun on 11/06/2017.
*/
public class ColumnHeader extends Cell {
public ColumnHeader(@NonNull String id, @Nullable String data) {
super(id, data);
}
}
================================================
FILE: app/src/main/java/com/evrencoskun/tableviewsample/tableview/model/RowHeader.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Evren Coşkun
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableviewsample.tableview.model;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
/**
* Created by evrencoskun on 11/06/2017.
*/
public class RowHeader extends Cell {
public RowHeader(@NonNull String id, @Nullable String data) {
super(id, data);
}
}
================================================
FILE: app/src/main/java/com/evrencoskun/tableviewsample/tableview/popup/ColumnHeaderLongPressPopup.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Evren Coşkun
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableviewsample.tableview.popup;
import android.content.Context;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.PopupMenu;
import androidx.annotation.NonNull;
import com.evrencoskun.tableview.TableView;
import com.evrencoskun.tableview.sort.SortState;
import com.evrencoskun.tableviewsample.R;
import com.evrencoskun.tableviewsample.tableview.holder.ColumnHeaderViewHolder;
/**
* Created by evrencoskun on 24.12.2017.
*/
public class ColumnHeaderLongPressPopup extends PopupMenu implements PopupMenu
.OnMenuItemClickListener {
// Menu Item constants
private static final int ASCENDING = 1;
private static final int DESCENDING = 2;
private static final int HIDE_ROW = 3;
private static final int SHOW_ROW = 4;
private static final int SCROLL_ROW = 5;
@NonNull
private final TableView mTableView;
private final int mXPosition;
public ColumnHeaderLongPressPopup(@NonNull ColumnHeaderViewHolder viewHolder, @NonNull TableView tableView) {
super(viewHolder.itemView.getContext(), viewHolder.itemView);
this.mTableView = tableView;
this.mXPosition = viewHolder.getBindingAdapterPosition();
initialize();
}
private void initialize() {
createMenuItem();
changeMenuItemVisibility();
this.setOnMenuItemClickListener(this);
}
private void createMenuItem() {
Context context = mTableView.getContext();
this.getMenu().add(Menu.NONE, ASCENDING, 0, context.getString(R.string.sort_ascending));
this.getMenu().add(Menu.NONE, DESCENDING, 1, context.getString(R.string.sort_descending));
this.getMenu().add(Menu.NONE, HIDE_ROW, 2, context.getString(R.string.hiding_row_sample));
this.getMenu().add(Menu.NONE, SHOW_ROW, 3, context.getString(R.string.showing_row_sample));
this.getMenu().add(Menu.NONE, SCROLL_ROW, 4, context.getString(R.string.scroll_to_row_position));
this.getMenu().add(Menu.NONE, SCROLL_ROW, 0, "change width");
// add new one ...
}
private void changeMenuItemVisibility() {
// Determine which one shouldn't be visible
SortState sortState = mTableView.getSortingStatus(mXPosition);
if (sortState == SortState.UNSORTED) {
// Show others
} else if (sortState == SortState.DESCENDING) {
// Hide DESCENDING menu item
getMenu().getItem(1).setVisible(false);
} else if (sortState == SortState.ASCENDING) {
// Hide ASCENDING menu item
getMenu().getItem(0).setVisible(false);
}
}
@Override
public boolean onMenuItemClick(MenuItem menuItem) {
// Note: item id is index of menu item..
switch (menuItem.getItemId()) {
case ASCENDING:
mTableView.sortColumn(mXPosition, SortState.ASCENDING);
break;
case DESCENDING:
mTableView.sortColumn(mXPosition, SortState.DESCENDING);
break;
case HIDE_ROW:
mTableView.hideRow(5);
break;
case SHOW_ROW:
mTableView.showRow(5);
break;
case SCROLL_ROW:
mTableView.scrollToRowPosition(5);
break;
}
return true;
}
}
================================================
FILE: app/src/main/java/com/evrencoskun/tableviewsample/tableview/popup/RowHeaderLongPressPopup.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Evren Coşkun
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableviewsample.tableview.popup;
import android.content.Context;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.PopupMenu;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.evrencoskun.tableview.TableView;
import com.evrencoskun.tableviewsample.R;
/**
* Created by evrencoskun on 21.01.2018.
*/
public class RowHeaderLongPressPopup extends PopupMenu implements PopupMenu
.OnMenuItemClickListener {
// Menu Item constants
private static final int SCROLL_COLUMN = 1;
private static final int SHOWHIDE_COLUMN = 2;
private static final int REMOVE_ROW = 3;
@NonNull
private final TableView mTableView;
private final int mRowPosition;
public RowHeaderLongPressPopup(@NonNull RecyclerView.ViewHolder viewHolder, @NonNull TableView tableView) {
super(viewHolder.itemView.getContext(), viewHolder.itemView);
this.mTableView = tableView;
this.mRowPosition = viewHolder.getBindingAdapterPosition();
initialize();
}
private void initialize() {
createMenuItem();
this.setOnMenuItemClickListener(this);
}
private void createMenuItem() {
Context context = mTableView.getContext();
this.getMenu().add(Menu.NONE, SCROLL_COLUMN, 0, context.getString(R.string
.scroll_to_column_position));
this.getMenu().add(Menu.NONE, SHOWHIDE_COLUMN, 1, context.getString(R.string
.show_hide_the_column));
this.getMenu().add(Menu.NONE, REMOVE_ROW, 2, "Remove " + mRowPosition + " position");
// add new one ...
}
@Override
public boolean onMenuItemClick(MenuItem menuItem) {
// Note: item id is index of menu item..
switch (menuItem.getItemId()) {
case SCROLL_COLUMN:
mTableView.scrollToColumnPosition(15);
break;
case SHOWHIDE_COLUMN:
int column = 1;
if (mTableView.isColumnVisible(column)) {
mTableView.hideColumn(column);
} else {
mTableView.showColumn(column);
}
break;
case REMOVE_ROW:
mTableView.getAdapter().removeRow(mRowPosition);
break;
}
return true;
}
}
================================================
FILE: app/src/main/res/drawable/ic_down.xml
================================================
================================================
FILE: app/src/main/res/drawable/ic_female.xml
================================================
================================================
FILE: app/src/main/res/drawable/ic_happy.xml
================================================
================================================
FILE: app/src/main/res/drawable/ic_male.xml
================================================
================================================
FILE: app/src/main/res/drawable/ic_next.xml
================================================
================================================
FILE: app/src/main/res/drawable/ic_previous.xml
================================================
================================================
FILE: app/src/main/res/drawable/ic_sad.xml
================================================
================================================
FILE: app/src/main/res/drawable/ic_up.xml
================================================
================================================
FILE: app/src/main/res/layout/activity_main.xml
================================================
================================================
FILE: app/src/main/res/layout/fragment_main.xml
================================================
================================================
FILE: app/src/main/res/layout/table_view_cell_layout.xml
================================================
================================================
FILE: app/src/main/res/layout/table_view_column_header_layout.xml
================================================
================================================
FILE: app/src/main/res/layout/table_view_corner_layout.xml
================================================
================================================
FILE: app/src/main/res/layout/table_view_image_cell_layout.xml
================================================
================================================
FILE: app/src/main/res/layout/table_view_row_header_layout.xml
================================================
================================================
FILE: app/src/main/res/values/colors.xml
================================================
#3F51B5#303F9F#FF4081#E7E7E7#ffffff@android:color/holo_red_light#0a0a0a
================================================
FILE: app/src/main/res/values/dimens.xml
================================================
40dp55dp12sp55dp40dp
================================================
FILE: app/src/main/res/values/strings.xml
================================================
TableViewSampleSort AscendingSort DescendingHiding 3. Row SampleShowing 3. Row SampleScroll to 15. column positionScroll to 5. row positionShowing / Hiding 1. columnPage %s, showing items %s - %s.HappySadMaleFemale10202550All
================================================
FILE: app/src/main/res/values/styles.xml
================================================
================================================
FILE: build.gradle
================================================
/*
* MIT License
*
* Copyright (c) 2021 Evren Coşkun
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.android_gradle_plugin_version = '4.2.1'
ext.androidx_annotation_version = '1.2.0'
ext.androidx_appcompat_version = '1.3.0'
ext.androidx_core_version = '1.5.0'
ext.androidx_fragment_version = '1.3.5'
ext.androidx_recyclerview_version = '1.2.1'
ext.androidx_test_version = '1.3.0'
ext.androidx_test_espresso_version = '3.3.0'
ext.androidx_test_ext_version = '1.1.2'
ext.compile_sdk_version = 29
ext.java_version = '1.8'
ext.junit_version = '4.13.2'
ext.min_sdk_version = 14
ext.target_sdk_version = 29
repositories {
jcenter()
google()
}
dependencies {
classpath "com.android.tools.build:gradle:$android_gradle_plugin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
google()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
subprojects {
if (project.getGradle().startParameter.taskNames.any{it.contains('bintrayUpload')} && project.name in ['tableview']) {
println project.name
apply plugin: 'maven'
gradle.taskGraph.whenReady { taskGraph ->
def pomTask = taskGraph.getAllTasks().find {
it.path == ":$project.name:generatePomFileForReleasePublication"
}
if (pomTask == null) println 'pomTask null'
if (pomTask == null) return
pomTask.doLast {
println 'Updating pom-file(s) with License info'
pomTask.outputs.files
.filter { File file ->
file.path.contains("publications") && file.name.matches("^pom-.+\\.xml\$")
}
.forEach { File file -> addLicense(file) }
}
}
}
}
static void addLicense(File pom) {
def licenseNode = new Node(null, "license")
licenseNode.append(new Node(null, "name", "MIT"))
licenseNode.append(new Node(null, "url", "https://github.com/evrencoskun/TableView/blob/master/LICENSE"))
def licensesNode = new Node(null, "licenses")
licensesNode.append(licenseNode)
def xml = new XmlParser().parse(pom)
xml.append(licensesNode)
def writer = new PrintWriter(new FileWriter(pom))
def printer = new XmlNodePrinter(writer)
printer.preserveWhitespace = true
printer.print(xml)
writer.close()
}
================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
#
# MIT License
#
# Copyright (c) 2021 Evren Coşkun
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
================================================
FILE: gradle.properties
================================================
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
android.useAndroidX=true
================================================
FILE: gradlew
================================================
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
exec "$JAVACMD" "$@"
================================================
FILE: gradlew.bat
================================================
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: settings.gradle
================================================
/*
* MIT License
*
* Copyright (c) 2021 Evren Coşkun
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
include ':app', ':tableview'
================================================
FILE: tableview/.gitignore
================================================
/build
================================================
FILE: tableview/build.gradle
================================================
apply plugin: 'com.android.library'
android {
compileSdkVersion compile_sdk_version
defaultConfig {
minSdkVersion min_sdk_version
targetSdkVersion target_sdk_version
versionCode 1
versionName '0.8.9.4'
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
testCoverageEnabled true
}
}
compileOptions {
sourceCompatibility = java_version
targetCompatibility = java_version
}
buildFeatures {
buildConfig = false
}
lintOptions {
abortOnError false
}
tasks.withType(Javadoc) {
options.addStringOption('Xdoclint:none', '-quiet')
options.addStringOption('encoding', 'UTF-8')
options.addStringOption('charSet', 'UTF-8')
}
}
dependencies {
implementation "androidx.annotation:annotation:$androidx_annotation_version"
implementation "androidx.recyclerview:recyclerview:$androidx_recyclerview_version"
testImplementation "junit:junit:$junit_version"
androidTestImplementation "androidx.appcompat:appcompat:$androidx_appcompat_version"
androidTestImplementation "androidx.test:rules:$androidx_test_version"
androidTestImplementation "androidx.test:runner:$androidx_test_version"
androidTestImplementation "androidx.test.espresso:espresso-core:$androidx_test_espresso_version"
androidTestImplementation "androidx.test.espresso:espresso-contrib:$androidx_test_espresso_version"
androidTestImplementation "androidx.test.ext:junit:$androidx_test_ext_version"
androidTestImplementation "junit:junit:$junit_version"
}
// Configure the publishing
apply plugin: 'maven-publish'
task androidJavadocs(type: Javadoc) {
source = android.sourceSets.main.java.srcDirs
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
android.libraryVariants.all { variant ->
if (variant.name == 'release') {
owner.classpath += variant.javaCompileProvider.get().classpath
}
}
exclude '**/R.html', '**/R.*.html', '**/index.html'
}
task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) {
archiveClassifier.set('javadoc')
from androidJavadocs.destinationDir
}
task androidSourcesJar(type: Jar) {
archiveClassifier.set('sources')
from android.sourceSets.main.java.srcDirs
}
afterEvaluate {
publishing {
publications {
release(MavenPublication) {
from components.release
groupId = 'com.evrencoskun.library'
artifactId = 'tableview'
version = android.defaultConfig.versionName
artifact androidJavadocsJar
artifact androidSourcesJar
pom {
name = 'TableView'
description = 'TableView is a powerful Android library for displaying complex data structures and rendering tabular data composed of rows, columns and cells.'
url = 'https://github.com/evrencoskun/TableView'
}
}
}
repositories {
maven {
url = 'https://api.bintray.com/content/evrencoskun/artifact-sandbox/'
}
}
}
}
================================================
FILE: tableview/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/evrencoskun/Library/Android/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
================================================
FILE: tableview/src/androidTest/AndroidManifest.xml
================================================
================================================
FILE: tableview/src/androidTest/java/com/evrencoskun/tableview/test/CornerLayoutTest.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Andrew Beck
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableview.test;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.assertion.PositionAssertions.isCompletelyAbove;
import static androidx.test.espresso.assertion.PositionAssertions.isCompletelyBelow;
import static androidx.test.espresso.assertion.PositionAssertions.isCompletelyLeftOf;
import static androidx.test.espresso.assertion.PositionAssertions.isCompletelyRightOf;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withParent;
import static androidx.test.espresso.matcher.ViewMatchers.withParentIndex;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.Matchers.allOf;
import android.widget.RelativeLayout;
import androidx.test.espresso.matcher.ViewMatchers;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.evrencoskun.tableview.ITableView;
import com.evrencoskun.tableview.TableView;
import com.evrencoskun.tableview.test.adapters.CornerTestAdapter;
import com.evrencoskun.tableview.test.data.SimpleData;
import com.evrencoskun.tableview.test.matchers.ViewWidthMatcher;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class CornerLayoutTest {
@Rule
public ActivityScenarioRule mActivityTestRule =
new ActivityScenarioRule<>(TestActivity.class);
@Test
public void testDefaultCorner() {
mActivityTestRule.getScenario()
.onActivity(activity -> {
activity.setContentView(R.layout.corner_default);
TableView tableView = activity.findViewById(R.id.tableview);
CornerTestAdapter cornerTestAdapter = new CornerTestAdapter();
tableView.setAdapter(cornerTestAdapter);
SimpleData simpleData = new SimpleData(5);
cornerTestAdapter.setAllItems(
simpleData.getColumnHeaders(), simpleData.getRowHeaders(), simpleData.getCells());
});
// Check that the corner view is created and visible
// The Corner view uses cell_layout which has RelativeLayout as top item
onView(withId(R.id.corner_view))
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)));
// Check that it is the expected corner view by checking the text
// The first child of the RelativeLayout is a textView (index starts at zero)
onView(allOf(withParent(withId(R.id.corner_view)), withParentIndex(0)))
.check(matches(withText("Corner")));
// Check that Corner to is to the right of the column headers
onView(withId(R.id.corner_view)).check(isCompletelyLeftOf(withId(R.id.ColumnHeaderRecyclerView)));
// Check that Corner to is to the above of the row headers
onView(withId(R.id.corner_view)).check(isCompletelyAbove(withId(R.id.RowHeaderRecyclerView)));
}
@Test
public void testTopLeftCorner() {
mActivityTestRule.getScenario()
.onActivity(activity -> {
activity.setContentView(R.layout.corner_top_left);
TableView tableView = activity.findViewById(R.id.tableview);
CornerTestAdapter cornerTestAdapter = new CornerTestAdapter();
tableView.setAdapter(cornerTestAdapter);
SimpleData simpleData = new SimpleData(5);
cornerTestAdapter.setAllItems(
simpleData.getColumnHeaders(), simpleData.getRowHeaders(), simpleData.getCells());
});
// Check that the corner view is created and visible
// The Corner view uses cell_layout which has RelativeLayout as top item
onView(withId(R.id.corner_view))
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)));
// Check that it is the expected corner view by checking the text
// The first child of the RelativeLayout is a textView (index starts at zero)
onView(allOf(withParent(withId(R.id.corner_view)), withParentIndex(0)))
.check(matches(withText("Corner")));
// Check that Corner to is to the right of the column headers
onView(withId(R.id.corner_view)).check(isCompletelyLeftOf(withId(R.id.ColumnHeaderRecyclerView)));
// Check that Corner to is to the above of the row headers
onView(withId(R.id.corner_view)).check(isCompletelyAbove(withId(R.id.RowHeaderRecyclerView)));
}
@Test
public void testBottomLeftCorner() {
mActivityTestRule.getScenario()
.onActivity(activity -> {
activity.setContentView(R.layout.corner_bottom_left);
TableView tableView = activity.findViewById(R.id.tableview);
CornerTestAdapter cornerTestAdapter = new CornerTestAdapter();
tableView.setAdapter(cornerTestAdapter);
SimpleData simpleData = new SimpleData(5);
cornerTestAdapter.setAllItems(
simpleData.getColumnHeaders(), simpleData.getRowHeaders(), simpleData.getCells());
});
// Check that the corner view is created and visible
// The Corner view uses cell_layout which has RelativeLayout as top item
onView(withId(R.id.corner_view))
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)));
// Check that it is the expected corner view by checking the text
// The first child of the RelativeLayout is a textView (index starts at zero)
onView(allOf(withParent(withId(R.id.corner_view)), withParentIndex(0)))
.check(matches(withText("Corner")));
// Check that Corner to is to the right of the column headers
onView(withId(R.id.corner_view)).check(isCompletelyLeftOf(withId(R.id.ColumnHeaderRecyclerView)));
// Check that Corner to is to the above of the row headers
onView(withId(R.id.corner_view)).check(isCompletelyBelow(withId(R.id.RowHeaderRecyclerView)));
}
@Test
public void testTopRightCorner() {
mActivityTestRule.getScenario()
.onActivity(activity -> {
activity.setContentView(R.layout.corner_top_right);
TableView tableView = activity.findViewById(R.id.tableview);
CornerTestAdapter cornerTestAdapter = new CornerTestAdapter();
tableView.setAdapter(cornerTestAdapter);
SimpleData simpleData = new SimpleData(5);
cornerTestAdapter.setAllItems(
simpleData.getColumnHeaders(), simpleData.getRowHeaders(), simpleData.getCells());
});
// Check that the corner view is created and visible
// The Corner view uses cell_layout which has RelativeLayout as top item
onView(withId(R.id.corner_view))
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)));
// Check that it is the expected corner view by checking the text
// The first child of the RelativeLayout is a textView (index starts at zero)
onView(allOf(withParent(withId(R.id.corner_view)), withParentIndex(0)))
.check(matches(withText("Corner")));
// Check that Corner to is to the right of the column headers
onView(withId(R.id.corner_view)).check(isCompletelyRightOf(withId(R.id.ColumnHeaderRecyclerView)));
// Check that Corner to is to the above of the row headers
onView(withId(R.id.corner_view)).check(isCompletelyAbove(withId(R.id.RowHeaderRecyclerView)));
}
@Test
public void testBottomRightCorner() {
mActivityTestRule.getScenario()
.onActivity(activity -> {
activity.setContentView(R.layout.corner_bottom_right);
TableView tableView = activity.findViewById(R.id.tableview);
CornerTestAdapter cornerTestAdapter = new CornerTestAdapter();
tableView.setAdapter(cornerTestAdapter);
SimpleData simpleData = new SimpleData(5);
cornerTestAdapter.setAllItems(
simpleData.getColumnHeaders(), simpleData.getRowHeaders(), simpleData.getCells());
});
// Check that the corner view is created and visible
// The Corner view uses cell_layout which has RelativeLayout as top item
onView(withId(R.id.corner_view))
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)));
// Check that it is the expected corner view by checking the text
// The first child of the RelativeLayout is a textView (index starts at zero)
onView(allOf(withParent(withId(R.id.corner_view)), withParentIndex(0)))
.check(matches(withText("Corner")));
// Check that Corner to is to the right of the column headers
onView(withId(R.id.corner_view)).check(isCompletelyRightOf(withId(R.id.ColumnHeaderRecyclerView)));
// Check that Corner to is to the above of the row headers
onView(withId(R.id.corner_view)).check(isCompletelyBelow(withId(R.id.RowHeaderRecyclerView)));
}
@Test
public void testCornerConstructor() {
mActivityTestRule.getScenario()
.onActivity(activity -> {
TableView tableView = new TableView(activity, false);
// initialize was false so set properties and call initialize
tableView.setCornerViewLocation(ITableView.CornerViewLocation.BOTTOM_LEFT);
tableView.initialize();
RelativeLayout rl = new RelativeLayout(activity);
rl.addView(tableView);
CornerTestAdapter cornerTestAdapter = new CornerTestAdapter();
tableView.setAdapter(cornerTestAdapter);
SimpleData simpleData = new SimpleData(5);
cornerTestAdapter.setAllItems(
simpleData.getColumnHeaders(), simpleData.getRowHeaders(), simpleData.getCells());
activity.setContentView(rl);
});
// Check that the corner view is created and visible
// The Corner view uses cell_layout which has RelativeLayout as top item
onView(withId(R.id.corner_view))
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)));
// Check that it is the expected corner view by checking the text
// The first child of the RelativeLayout is a textView (index starts at zero)
onView(allOf(withParent(withId(R.id.corner_view)), withParentIndex(0)))
.check(matches(withText("Corner")));
// Check that Corner to is to the right of the column headers
onView(withId(R.id.corner_view)).check(isCompletelyLeftOf(withId(R.id.ColumnHeaderRecyclerView)));
// Check that Corner to is to the above of the row headers
onView(withId(R.id.corner_view)).check(isCompletelyBelow(withId(R.id.RowHeaderRecyclerView)));
}
@Test
public void testSetRowHeaderWidthLeft() {
mActivityTestRule.getScenario()
.onActivity(activity -> {
activity.setContentView(R.layout.corner_top_left);
TableView tableView = activity.findViewById(R.id.tableview);
CornerTestAdapter cornerTestAdapter = new CornerTestAdapter();
tableView.setAdapter(cornerTestAdapter);
SimpleData simpleData = new SimpleData(5);
cornerTestAdapter.setAllItems(
simpleData.getColumnHeaders(), simpleData.getRowHeaders(), simpleData.getCells());
// Set a new width on row header
tableView.setRowHeaderWidth(200);
});
// Check that the corner view is created and visible
// The Corner view uses cell_layout which has RelativeLayout as top item
onView(withId(R.id.corner_view))
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)));
// Check that it is the expected corner view by checking the text
// The first child of the RelativeLayout is a textView (index starts at zero)
onView(allOf(withParent(withId(R.id.corner_view)), withParentIndex(0)))
.check(matches(withText("Corner")));
// Check that Corner to is to the right of the column headers
onView(withId(R.id.corner_view)).check(isCompletelyLeftOf(withId(R.id.ColumnHeaderRecyclerView)));
// Check that Corner to is to the above of the row headers
onView(withId(R.id.corner_view)).check(isCompletelyAbove(withId(R.id.RowHeaderRecyclerView)));
// Check that the corner is new width
onView(withId(R.id.corner_view)).check(matches(new ViewWidthMatcher(200)));
// Check that the row header is new width
onView(withId(R.id.RowHeaderRecyclerView)).check(matches(new ViewWidthMatcher(200)));
}
@Test
public void testSetRowHeaderWidthRight() {
mActivityTestRule.getScenario()
.onActivity(activity -> {
activity.setContentView(R.layout.corner_top_right);
TableView tableView = activity.findViewById(R.id.tableview);
CornerTestAdapter cornerTestAdapter = new CornerTestAdapter();
tableView.setAdapter(cornerTestAdapter);
SimpleData simpleData = new SimpleData(5);
cornerTestAdapter.setAllItems(
simpleData.getColumnHeaders(), simpleData.getRowHeaders(), simpleData.getCells());
// Set a new width on row header
tableView.setRowHeaderWidth(200);
});
// Check that the corner view is created and visible
// The Corner view uses cell_layout which has RelativeLayout as top item
onView(withId(R.id.corner_view))
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)));
// Check that it is the expected corner view by checking the text
// The first child of the RelativeLayout is a textView (index starts at zero)
onView(allOf(withParent(withId(R.id.corner_view)), withParentIndex(0)))
.check(matches(withText("Corner")));
// Check that Corner to is to the right of the column headers
onView(withId(R.id.corner_view)).check(isCompletelyRightOf(withId(R.id.ColumnHeaderRecyclerView)));
// Check that Corner to is to the above of the row headers
onView(withId(R.id.corner_view)).check(isCompletelyAbove(withId(R.id.RowHeaderRecyclerView)));
// Check that the corner is new width
onView(withId(R.id.corner_view)).check(matches(new ViewWidthMatcher(200)));
// Check that the row header is new width
onView(withId(R.id.RowHeaderRecyclerView)).check(matches(new ViewWidthMatcher(200)));
}
}
================================================
FILE: tableview/src/androidTest/java/com/evrencoskun/tableview/test/CornerViewTest.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Andrew Beck
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableview.test;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.Visibility;
import static androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import android.widget.RelativeLayout;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.evrencoskun.tableview.TableView;
import com.evrencoskun.tableview.test.adapters.SimpleTestAdapter;
import com.evrencoskun.tableview.test.data.SimpleData;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class CornerViewTest {
@Rule
public ActivityScenarioRule mActivityTestRule =
new ActivityScenarioRule<>(TestActivity.class);
@Test
public void testEmptyTable() {
mActivityTestRule.getScenario()
.onActivity(activity -> {
TableView tableView = new TableView(activity);
RelativeLayout rl = new RelativeLayout(activity);
rl.addView(tableView);
SimpleTestAdapter simpleTestAdapter = new SimpleTestAdapter();
tableView.setAdapter(simpleTestAdapter);
SimpleData simpleData = new SimpleData(0);
simpleTestAdapter.setAllItems(simpleData.getColumnHeaders(), simpleData.getRowHeaders(),
simpleData.getCells());
activity.setContentView(rl);
// Check that the corner view is not created (therefore not shown)
Assert.assertNull(simpleTestAdapter.getCornerView());
});
}
@Test
public void testEmptyTableResetNonEmptyTable() {
mActivityTestRule.getScenario()
.onActivity(activity -> {
TableView tableView = new TableView(activity);
RelativeLayout rl = new RelativeLayout(activity);
rl.addView(tableView);
SimpleTestAdapter simpleTestAdapter = new SimpleTestAdapter();
tableView.setAdapter(simpleTestAdapter);
SimpleData simpleData = new SimpleData(0);
simpleTestAdapter.setAllItems(simpleData.getColumnHeaders(), simpleData.getRowHeaders(),
simpleData.getCells());
activity.setContentView(rl);
// Check that the corner view is not created (therefore not shown)
Assert.assertNull(simpleTestAdapter.getCornerView());
// Change the items of data to reset
SimpleData simpleDataReset = new SimpleData(2);
simpleTestAdapter.setAllItems(simpleDataReset.getColumnHeaders(), simpleDataReset.getRowHeaders(),
simpleDataReset.getCells());
// Check that the corner view is now created
Assert.assertNotNull(simpleTestAdapter.getCornerView());
});
// Check the corner view is now visible
onView(withId(R.id.corner_view))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)));
// Check that it is the expected corner view by checking the text
onView(withId(R.id.corner_text))
.check(matches(withText("Corner")));
}
@Test
public void testEmptyTableResetEmptyTable() {
mActivityTestRule.getScenario()
.onActivity(activity -> {
TableView tableView = new TableView(activity);
RelativeLayout rl = new RelativeLayout(activity);
rl.addView(tableView);
SimpleTestAdapter simpleTestAdapter = new SimpleTestAdapter();
tableView.setAdapter(simpleTestAdapter);
SimpleData simpleData = new SimpleData(0);
simpleTestAdapter.setAllItems(simpleData.getColumnHeaders(), simpleData.getRowHeaders(),
simpleData.getCells());
activity.setContentView(rl);
// Check that the corner view is not created (therefore not shown)
Assert.assertNull(simpleTestAdapter.getCornerView());
// Change the items of data to reset
SimpleData simpleDataReset = new SimpleData(0);
simpleTestAdapter.setAllItems(simpleDataReset.getColumnHeaders(), simpleDataReset.getRowHeaders(),
simpleDataReset.getCells());
// Check that the corner view is still not created
Assert.assertNull(simpleTestAdapter.getCornerView());
});
}
@Test
public void testNonEmptyTable() {
mActivityTestRule.getScenario()
.onActivity(activity -> {
TableView tableView = new TableView(activity);
RelativeLayout rl = new RelativeLayout(activity);
rl.addView(tableView);
SimpleTestAdapter simpleTestAdapter = new SimpleTestAdapter();
tableView.setAdapter(simpleTestAdapter);
SimpleData simpleData = new SimpleData(1);
simpleTestAdapter.setAllItems(simpleData.getColumnHeaders(), simpleData.getRowHeaders(),
simpleData.getCells());
activity.setContentView(rl);
// Check that the corner view is created
Assert.assertNotNull(simpleTestAdapter.getCornerView());
});
// Check the corner view is now visible
onView(withId(R.id.corner_view))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)));
// Check that it is the expected corner view by checking the text
onView(withId(R.id.corner_text))
.check(matches(withText("Corner")));
}
@Test
public void testNonEmptyTableResetNonEmpty() {
mActivityTestRule.getScenario()
.onActivity(activity -> {
TableView tableView = new TableView(activity);
tableView.setId(R.id.tableview);
RelativeLayout rl = new RelativeLayout(activity);
rl.addView(tableView);
SimpleTestAdapter simpleTestAdapter = new SimpleTestAdapter();
tableView.setAdapter(simpleTestAdapter);
SimpleData simpleData = new SimpleData(1);
simpleTestAdapter.setAllItems(simpleData.getColumnHeaders(), simpleData.getRowHeaders(),
simpleData.getCells());
activity.setContentView(rl);
// Check that the corner view is created before resetting to empty
Assert.assertNotNull(simpleTestAdapter.getCornerView());
});
// Check the corner view is visible
onView(withId(R.id.corner_view))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)));
mActivityTestRule.getScenario()
.onActivity(activity -> {
TableView tableView = activity.findViewById(R.id.tableview);
SimpleTestAdapter simpleTestAdapter = (SimpleTestAdapter) tableView.getAdapter();
// Change the items of data to reset
SimpleData simpleDataReset = new SimpleData(2);
simpleTestAdapter.setAllItems(simpleDataReset.getColumnHeaders(), simpleDataReset.getRowHeaders(),
simpleDataReset.getCells());
// Check that the corner view is still created
Assert.assertNotNull(simpleTestAdapter.getCornerView());
});
// Check the corner view is still visible
onView(withId(R.id.corner_view))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)));
// Check that it is the expected corner view by checking the text
onView(withId(R.id.corner_text))
.check(matches(withText("Corner")));
}
@Test
public void testNonEmptyTableResetEmpty() {
mActivityTestRule.getScenario()
.onActivity(activity -> {
TableView tableView = new TableView(activity);
RelativeLayout rl = new RelativeLayout(activity);
rl.addView(tableView);
SimpleTestAdapter simpleTestAdapter = new SimpleTestAdapter();
tableView.setAdapter(simpleTestAdapter);
SimpleData simpleData = new SimpleData(1);
simpleTestAdapter.setAllItems(simpleData.getColumnHeaders(), simpleData.getRowHeaders(),
simpleData.getCells());
// Check that the corner view is created before resetting to empty
Assert.assertNotNull(simpleTestAdapter.getCornerView());
// Change the items of data to reset
SimpleData simpleDataReset = new SimpleData(0);
simpleTestAdapter.setAllItems(simpleDataReset.getColumnHeaders(), simpleDataReset.getRowHeaders(),
simpleDataReset.getCells());
activity.setContentView(rl);
// Check that the corner view is still created but visibility is gone
Assert.assertNotNull(simpleTestAdapter.getCornerView());
});
// Check the corner view visibility is GONE
onView(withId(R.id.corner_view))
.check(matches(withEffectiveVisibility(Visibility.GONE)));
}
@Test
public void testColumnHeadersOnlyTable() {
mActivityTestRule.getScenario()
.onActivity(activity -> {
TableView tableView = new TableView(activity);
RelativeLayout rl = new RelativeLayout(activity);
rl.addView(tableView);
SimpleTestAdapter simpleTestAdapter = new SimpleTestAdapter();
tableView.setAdapter(simpleTestAdapter);
// Only want column headers
SimpleData simpleData = new SimpleData(5, 0);
simpleTestAdapter.setAllItems(simpleData.getColumnHeaders(), simpleData.getRowHeaders(),
simpleData.getCells());
activity.setContentView(rl);
// Check that the corner view is not created
Assert.assertNull(simpleTestAdapter.getCornerView());
});
}
@Test
public void testColumnHeadersOnlyTableResetNonEmptyTable() {
mActivityTestRule.getScenario()
.onActivity(activity -> {
TableView tableView = new TableView(activity);
RelativeLayout rl = new RelativeLayout(activity);
rl.addView(tableView);
SimpleTestAdapter simpleTestAdapter = new SimpleTestAdapter();
tableView.setAdapter(simpleTestAdapter);
// Only want column headers
SimpleData simpleData = new SimpleData(5, 0);
simpleTestAdapter.setAllItems(simpleData.getColumnHeaders(), simpleData.getRowHeaders(),
simpleData.getCells());
// Check that the corner view is not created
Assert.assertNull(simpleTestAdapter.getCornerView());
// Change the items of data to reset
SimpleData simpleDataReset = new SimpleData(5);
simpleTestAdapter.setAllItems(simpleDataReset.getColumnHeaders(), simpleDataReset.getRowHeaders(),
simpleDataReset.getCells());
activity.setContentView(rl);
// Check that the corner view is not created
Assert.assertNotNull(simpleTestAdapter.getCornerView());
});
// Check the corner view is now visible
onView(withId(R.id.corner_view))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)));
// Check that it is the expected corner view by checking the text
onView(withId(R.id.corner_text))
.check(matches(withText("Corner")));
}
@Test
public void testColumnHeadersOnlyTableResetEmptyTable() {
mActivityTestRule.getScenario()
.onActivity(activity -> {
TableView tableView = new TableView(activity);
RelativeLayout rl = new RelativeLayout(activity);
rl.addView(tableView);
SimpleTestAdapter simpleTestAdapter = new SimpleTestAdapter();
tableView.setAdapter(simpleTestAdapter);
// Only want column headers
SimpleData simpleData = new SimpleData(5, 0);
simpleTestAdapter.setAllItems(simpleData.getColumnHeaders(), simpleData.getRowHeaders(),
simpleData.getCells());
// Check that the corner view is not created
Assert.assertNull(simpleTestAdapter.getCornerView());
// Change the items of data to reset
SimpleData simpleDataReset = new SimpleData(0);
simpleTestAdapter.setAllItems(simpleDataReset.getColumnHeaders(), simpleDataReset.getRowHeaders(),
simpleDataReset.getCells());
activity.setContentView(rl);
// Check that the corner view is not created
Assert.assertNull(simpleTestAdapter.getCornerView());
});
}
@Test
public void testColumnHeadersOnlyTableShowCorner() {
mActivityTestRule.getScenario()
.onActivity(activity -> {
TableView tableView = new TableView(activity);
RelativeLayout rl = new RelativeLayout(activity);
rl.addView(tableView);
// Set the option to show corner view when there is not row data
tableView.setShowCornerView(true);
SimpleTestAdapter simpleTestAdapter = new SimpleTestAdapter();
tableView.setAdapter(simpleTestAdapter);
// Only want column headers
SimpleData simpleData = new SimpleData(5, 0);
simpleTestAdapter.setAllItems(simpleData.getColumnHeaders(), simpleData.getRowHeaders(),
simpleData.getCells());
activity.setContentView(rl);
// Check that the corner view is created
Assert.assertNotNull(simpleTestAdapter.getCornerView());
});
// Check the corner view is now visible
onView(withId(R.id.corner_view))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)));
// Check that it is the expected corner view by checking the text
onView(withId(R.id.corner_text))
.check(matches(withText("Corner")));
}
@Test
public void testColumnHeadersOnlyTableShowCornerResetEmptyTable() {
mActivityTestRule.getScenario()
.onActivity(activity -> {
TableView tableView = new TableView(activity);
RelativeLayout rl = new RelativeLayout(activity);
rl.addView(tableView);
// Set the option to show corner view when there is not row data
tableView.setShowCornerView(true);
SimpleTestAdapter simpleTestAdapter = new SimpleTestAdapter();
tableView.setAdapter(simpleTestAdapter);
// Only want column headers
SimpleData simpleData = new SimpleData(5, 0);
simpleTestAdapter.setAllItems(simpleData.getColumnHeaders(), simpleData.getRowHeaders(),
simpleData.getCells());
// Check that the corner view is created
Assert.assertNotNull(simpleTestAdapter.getCornerView());
// Change the items of data to reset
SimpleData simpleDataReset = new SimpleData(0);
simpleTestAdapter.setAllItems(simpleDataReset.getColumnHeaders(), simpleDataReset.getRowHeaders(),
simpleDataReset.getCells());
activity.setContentView(rl);
// Check that the corner view is still created
Assert.assertNotNull(simpleTestAdapter.getCornerView());
});
// Check the corner view visibility is GONE
onView(withId(R.id.corner_view))
.check(matches(withEffectiveVisibility(Visibility.GONE)));
}
@Test
public void testColumnHeadersOnlyTableShowCornerResetNonEmptyTable() {
mActivityTestRule.getScenario()
.onActivity(activity -> {
TableView tableView = new TableView(activity);
RelativeLayout rl = new RelativeLayout(activity);
rl.addView(tableView);
// Set the option to show corner view when there is not row data
tableView.setShowCornerView(true);
SimpleTestAdapter simpleTestAdapter = new SimpleTestAdapter();
tableView.setAdapter(simpleTestAdapter);
// Only want column headers
SimpleData simpleData = new SimpleData(5, 0);
simpleTestAdapter.setAllItems(simpleData.getColumnHeaders(), simpleData.getRowHeaders(),
simpleData.getCells());
// Check that the corner view is created
Assert.assertNotNull(simpleTestAdapter.getCornerView());
// Change the items of data to reset
SimpleData simpleDataReset = new SimpleData(2);
simpleTestAdapter.setAllItems(simpleDataReset.getColumnHeaders(), simpleDataReset.getRowHeaders(),
simpleDataReset.getCells());
activity.setContentView(rl);
// Check that the corner view is still created
Assert.assertNotNull(simpleTestAdapter.getCornerView());
});
// Check the corner view visibility is VISIBLE
onView(withId(R.id.corner_view))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)));
}
}
================================================
FILE: tableview/src/androidTest/java/com/evrencoskun/tableview/test/ReverseLayoutTest.java
================================================
/*
* MIT License
*
* Copyright (c) 2020 Andrew Beck
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableview.test;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.assertion.PositionAssertions.isCompletelyLeftOf;
import static androidx.test.espresso.assertion.PositionAssertions.isCompletelyRightOf;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.Visibility;
import static androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withParent;
import static androidx.test.espresso.matcher.ViewMatchers.withParentIndex;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.Matchers.allOf;
import android.widget.RelativeLayout;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.evrencoskun.tableview.ITableView;
import com.evrencoskun.tableview.TableView;
import com.evrencoskun.tableview.test.adapters.CornerTestAdapter;
import com.evrencoskun.tableview.test.data.SimpleData;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class ReverseLayoutTest {
@Rule
public ActivityScenarioRule mActivityTestRule =
new ActivityScenarioRule<>(TestActivity.class);
@Test
public void testDefaultLayout() {
mActivityTestRule.getScenario()
.onActivity(activity -> {
activity.setContentView(R.layout.corner_default);
TableView tableView = activity.findViewById(R.id.tableview);
Assert.assertNotNull(tableView);
CornerTestAdapter cornerTestAdapter = new CornerTestAdapter();
tableView.setAdapter(cornerTestAdapter);
SimpleData simpleData = new SimpleData(5);
cornerTestAdapter.setAllItems(
simpleData.getColumnHeaders(), simpleData.getRowHeaders(), simpleData.getCells());
});
// Check that the corner view is created and visible
// The Corner view uses cell_layout which has RelativeLayout as top item
onView(withId(R.id.corner_view))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)));
// Check that it is the expected corner view by checking the text
// The first child of the RelativeLayout is a textView (index starts at zero)
onView(allOf(withParent(withId(R.id.corner_view)), withParentIndex(0)))
.check(matches(withText("Corner")));
// Check that column headers are ordered Left to Right
onView(withText("c:0")).check(isCompletelyLeftOf(withText("c:1")));
// Check that first cell data row are ordered Left to Right
onView(withText("r:0c:0")).check(isCompletelyLeftOf(withText("r:0c:1")));
}
@Test
public void testReverseLayout() {
mActivityTestRule.getScenario()
.onActivity(activity -> {
activity.setContentView(R.layout.reverse_layout);
TableView tableView = activity.findViewById(R.id.tableview);
Assert.assertNotNull(tableView);
CornerTestAdapter cornerTestAdapter = new CornerTestAdapter();
tableView.setAdapter(cornerTestAdapter);
SimpleData simpleData = new SimpleData(5);
cornerTestAdapter.setAllItems(
simpleData.getColumnHeaders(), simpleData.getRowHeaders(), simpleData.getCells());
});
// Check that the corner view is created and visible
// The Corner view uses cell_layout which has RelativeLayout as top item
onView(withId(R.id.corner_view))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)));
// Check that it is the expected corner view by checking the text
// The first child of the RelativeLayout is a textView (index starts at zero)
onView(allOf(withParent(withId(R.id.corner_view)), withParentIndex(0)))
.check(matches(withText("Corner")));
// Check that column headers are ordered Right to Left
onView(withText("c:0")).check(isCompletelyRightOf(withText("c:1")));
// Check that first cell data row are ordered Right to Left
onView(withText("r:0c:0")).check(isCompletelyRightOf(withText("r:0c:1")));
}
@Test
public void testReverseConstructor() {
mActivityTestRule.getScenario()
.onActivity(activity -> {
TableView tableView = new TableView(activity, false);
// initialize was false so set properties and call initialize
//Set CornerView to Top Right and ReverseLayout = true
tableView.setCornerViewLocation(ITableView.CornerViewLocation.TOP_RIGHT);
tableView.setReverseLayout(true);
tableView.initialize();
RelativeLayout rl = new RelativeLayout(activity);
rl.addView(tableView);
CornerTestAdapter cornerTestAdapter = new CornerTestAdapter();
tableView.setAdapter(cornerTestAdapter);
SimpleData simpleData = new SimpleData(5);
cornerTestAdapter.setAllItems(
simpleData.getColumnHeaders(), simpleData.getRowHeaders(), simpleData.getCells());
activity.setContentView(rl);
});
// Check that the corner view is created and visible
// The Corner view uses cell_layout which has RelativeLayout as top item
onView(withId(R.id.corner_view))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)));
// Check that it is the expected corner view by checking the text
// The first child of the RelativeLayout is a textView (index starts at zero)
onView(allOf(withParent(withId(R.id.corner_view)), withParentIndex(0)))
.check(matches(withText("Corner")));
// Check that column headers are ordered Right to Left
onView(withText("c:0")).check(isCompletelyRightOf(withText("c:1")));
// Check that first cell data row are ordered Right to Left
onView(withText("r:0c:0")).check(isCompletelyRightOf(withText("r:0c:1")));
}
}
================================================
FILE: tableview/src/androidTest/java/com/evrencoskun/tableview/test/SimpleActivityTest.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Andrew Beck
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableview.test;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withParent;
import static androidx.test.espresso.matcher.ViewMatchers.withParentIndex;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.Matchers.allOf;
import android.view.View;
import android.widget.RelativeLayout;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;
import com.evrencoskun.tableview.TableView;
import com.evrencoskun.tableview.test.adapters.SimpleTestAdapter;
import com.evrencoskun.tableview.test.data.SimpleData;
import org.hamcrest.Matcher;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class SimpleActivityTest {
@Rule
public ActivityScenarioRule mActivityTestRule =
new ActivityScenarioRule<>(TestActivity.class);
@Test
public void testDefaults() {
TableView tableView =
new TableView(InstrumentationRegistry.getInstrumentation().getTargetContext());
Assert.assertFalse(tableView.isAllowClickInsideCell());
Assert.assertTrue(tableView.isShowHorizontalSeparators());
Assert.assertTrue(tableView.isShowVerticalSeparators());
}
@Test
public void testSmallTable() {
mActivityTestRule.getScenario()
.onActivity(activity -> {
TableView tableView = new TableView(activity);
tableView.setId(R.id.tableview);
RelativeLayout rl = new RelativeLayout(activity);
rl.addView(tableView);
SimpleTestAdapter simpleTestAdapter = new SimpleTestAdapter();
tableView.setAdapter(simpleTestAdapter);
SimpleData simpleData = new SimpleData(5);
simpleTestAdapter.setAllItems(
simpleData.getColumnHeaders(), simpleData.getRowHeaders(), simpleData.getCells());
activity.setContentView(rl);
});
// Check that the row header was created as expected at 5th Row (index starts at zero)
// cell_layout has LinearLayout as top item
Matcher rowHeaders = allOf(withParent(withId(R.id.tableview)), withParentIndex(1));
Matcher rowHeader = allOf(withParent(rowHeaders), withParentIndex(4));
onView(allOf(withParent(rowHeader), withParentIndex(0)))
.check(matches(withText("r:4")));
// Check that the column header was created as expected at 5th Row (index starts at zero)
// cell_layout has LinearLayout as top item
Matcher columnHeaders = allOf(withParent(withId(R.id.tableview)), withParentIndex(0));
Matcher columnHeader = allOf(withParent(columnHeaders), withParentIndex(4));
onView(allOf(withParent(columnHeader), withParentIndex(0)))
.check(matches(withText("c:4")));
}
}
================================================
FILE: tableview/src/androidTest/java/com/evrencoskun/tableview/test/TestActivity.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Andrew Beck
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableview.test;
import android.app.Activity;
public class TestActivity extends Activity {
}
================================================
FILE: tableview/src/androidTest/java/com/evrencoskun/tableview/test/adapters/AbstractTableAdapterTest.java
================================================
package com.evrencoskun.tableview.test.adapters;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static java.util.Collections.emptyList;
import android.view.View;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;
import com.evrencoskun.tableview.TableView;
import com.evrencoskun.tableview.test.TestActivity;
import com.evrencoskun.tableview.test.data.SimpleData;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class AbstractTableAdapterTest {
@Rule
public ActivityScenarioRule mActivityTestRule =
new ActivityScenarioRule<>(TestActivity.class);
private SimpleData mData;
private TableView mTableView;
private SimpleTestAdapter mAdapter;
@Before
public void before() {
mData = new SimpleData(5);
mTableView = new TableView(InstrumentationRegistry.getInstrumentation().getContext());
mAdapter = new SimpleTestAdapter();
mAdapter.setTableView(mTableView);
}
@Test
public void testCornerViewStateWithDisabledCorner() {
mTableView.setShowCornerView(false);
assertNull(mAdapter.getCornerView());
mAdapter.setAllItems(null, null, null);
assertNull(mAdapter.getCornerView());
mAdapter.setAllItems(mData.getColumnHeaders(), mData.getRowHeaders(), mData.getCells());
assertNotNull(mAdapter.getCornerView());
assertEquals(View.VISIBLE, mAdapter.getCornerView().getVisibility());
mAdapter.setAllItems(emptyList(), emptyList(), emptyList());
assertNotNull(mAdapter.getCornerView());
assertEquals(View.GONE, mAdapter.getCornerView().getVisibility());
}
@Test
public void testCornerViewStateWithEnabledCorners() {
mTableView.setShowCornerView(true);
assertNull(mAdapter.getCornerView());
mAdapter.setAllItems(mData.getColumnHeaders(), mData.getRowHeaders(), mData.getCells());
assertNotNull(mAdapter.getCornerView());
assertEquals(View.VISIBLE, mAdapter.getCornerView().getVisibility());
mAdapter.setAllItems(null, null, null);
assertNotNull(mAdapter.getCornerView());
assertEquals(View.GONE, mAdapter.getCornerView().getVisibility());
// We set some data, that we then reset to empty
mAdapter.setAllItems(mData.getColumnHeaders(), mData.getRowHeaders(), mData.getCells());
mAdapter.setAllItems(emptyList(), emptyList(), emptyList());
assertNotNull(mAdapter.getCornerView());
assertEquals(View.GONE, mAdapter.getCornerView().getVisibility());
mAdapter.setAllItems(mData.getColumnHeaders(), null, null);
assertNotNull(mAdapter.getCornerView());
assertEquals(View.VISIBLE, mAdapter.getCornerView().getVisibility());
mAdapter.setAllItems(null, mData.getRowHeaders(), null);
assertNotNull(mAdapter.getCornerView());
assertEquals(View.GONE, mAdapter.getCornerView().getVisibility());
mAdapter.setAllItems(mData.getColumnHeaders(), mData.getRowHeaders(), null);
assertNotNull(mAdapter.getCornerView());
assertEquals(View.VISIBLE, mAdapter.getCornerView().getVisibility());
}
@Test
public void testCornerViewStateWithToggledCorners() {
mTableView.setShowCornerView(true);
assertNull(mAdapter.getCornerView());
mAdapter.setAllItems(mData.getColumnHeaders(), mData.getRowHeaders(), mData.getCells());
assertNotNull(mAdapter.getCornerView());
assertEquals(View.VISIBLE, mAdapter.getCornerView().getVisibility());
mTableView.setShowCornerView(false);
mAdapter.setAllItems(mData.getColumnHeaders(), mData.getRowHeaders(), mData.getCells());
assertNotNull(mAdapter.getCornerView());
assertEquals(View.VISIBLE, mAdapter.getCornerView().getVisibility());
mAdapter.setAllItems(null, null, null);
assertNotNull(mAdapter.getCornerView());
assertEquals(View.GONE, mAdapter.getCornerView().getVisibility());
// We set some data, that we then reset to empty
mAdapter.setAllItems(mData.getColumnHeaders(), mData.getRowHeaders(), mData.getCells());
mAdapter.setAllItems(emptyList(), emptyList(), emptyList());
assertNotNull(mAdapter.getCornerView());
assertEquals(View.GONE, mAdapter.getCornerView().getVisibility());
mAdapter.setAllItems(mData.getColumnHeaders(), null, null);
assertNotNull(mAdapter.getCornerView());
assertEquals(View.GONE, mAdapter.getCornerView().getVisibility());
mAdapter.setAllItems(null, mData.getRowHeaders(), null);
assertNotNull(mAdapter.getCornerView());
assertEquals(View.GONE, mAdapter.getCornerView().getVisibility());
mAdapter.setAllItems(mData.getColumnHeaders(), mData.getRowHeaders(), null);
assertNotNull(mAdapter.getCornerView());
assertEquals(View.VISIBLE, mAdapter.getCornerView().getVisibility());
}
}
================================================
FILE: tableview/src/androidTest/java/com/evrencoskun/tableview/test/adapters/CornerTestAdapter.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Andrew Beck
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableview.test.adapters;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import com.evrencoskun.tableview.test.R;
import com.evrencoskun.tableview.adapter.AbstractTableAdapter;
import com.evrencoskun.tableview.adapter.recyclerview.holder.AbstractViewHolder;
import com.evrencoskun.tableview.test.models.Cell;
import com.evrencoskun.tableview.test.models.ColumnHeader;
import com.evrencoskun.tableview.test.models.RowHeader;
public class CornerTestAdapter extends AbstractTableAdapter {
static class TestCellViewHolder extends AbstractViewHolder {
final LinearLayout cell_container;
final TextView cell_textview;
TestCellViewHolder(View itemView) {
super(itemView);
cell_container = itemView.findViewById(com.evrencoskun.tableview.test.R.id.cell_container);
cell_textview = itemView.findViewById(com.evrencoskun.tableview.test.R.id.cell_data);
}
}
@NonNull
public AbstractViewHolder onCreateCellViewHolder(@NonNull ViewGroup parent, int viewType) {
View layout = LayoutInflater.from(parent.getContext())
.inflate(R.layout.cell_layout, parent, false);
return new TestCellViewHolder(layout);
}
public void onBindCellViewHolder(@NonNull AbstractViewHolder holder, Cell cell, int
columnPosition, int rowPosition) {
TestCellViewHolder viewHolder = (TestCellViewHolder) holder;
viewHolder.cell_textview.setText(cell.getData() != null ? cell.getData().toString() : "");
viewHolder.cell_container.getLayoutParams().width = LinearLayout.LayoutParams.WRAP_CONTENT;
viewHolder.cell_textview.requestLayout();
}
static class TestColumnHeaderViewHolder extends AbstractViewHolder {
final LinearLayout column_header_container;
final TextView cell_textview;
public TestColumnHeaderViewHolder(View itemView) {
super(itemView);
column_header_container = itemView.findViewById(R.id.column_header_container);
cell_textview = itemView.findViewById(R.id.column_header_textView);
}
}
@NonNull
public AbstractViewHolder onCreateColumnHeaderViewHolder(@NonNull ViewGroup parent, int
viewType) {
View layout = LayoutInflater.from(parent.getContext())
.inflate(R.layout.column_layout, parent, false);
return new TestColumnHeaderViewHolder(layout);
}
public void onBindColumnHeaderViewHolder(@NonNull AbstractViewHolder holder,
ColumnHeader columnHeader, int position) {
TestColumnHeaderViewHolder viewHolder = (TestColumnHeaderViewHolder) holder;
if (columnHeader.getData() != null)
viewHolder.cell_textview.setText(columnHeader.getData().toString());
viewHolder.column_header_container.getLayoutParams().width = LinearLayout.LayoutParams.WRAP_CONTENT;
viewHolder.cell_textview.requestLayout();
}
static class TestRowHeaderViewHolder extends AbstractViewHolder {
final TextView cell_textview;
public TestRowHeaderViewHolder(View itemView) {
super(itemView);
cell_textview = itemView.findViewById(R.id.row_header_textView);
}
}
@NonNull
public AbstractViewHolder onCreateRowHeaderViewHolder(@NonNull ViewGroup parent, int viewType) {
View layout = LayoutInflater.from(parent.getContext())
.inflate(R.layout.row_layout, parent, false);
return new TestRowHeaderViewHolder(layout);
}
public void onBindRowHeaderViewHolder(@NonNull AbstractViewHolder holder,
RowHeader rowHeader, int position) {
TestRowHeaderViewHolder viewHolder = (TestRowHeaderViewHolder) holder;
if (rowHeader.getData() != null)
viewHolder.cell_textview.setText(rowHeader.getData().toString());
}
@NonNull
public View onCreateCornerView(@NonNull ViewGroup parent) {
return LayoutInflater.from(parent.getContext())
.inflate(R.layout.corner_layout, parent, false);
}
}
================================================
FILE: tableview/src/androidTest/java/com/evrencoskun/tableview/test/adapters/SimpleTestAdapter.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Andrew Beck
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableview.test.adapters;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import com.evrencoskun.tableview.test.R;
import com.evrencoskun.tableview.adapter.AbstractTableAdapter;
import com.evrencoskun.tableview.adapter.recyclerview.holder.AbstractViewHolder;
import com.evrencoskun.tableview.test.models.Cell;
import com.evrencoskun.tableview.test.models.ColumnHeader;
import com.evrencoskun.tableview.test.models.RowHeader;
public class SimpleTestAdapter extends AbstractTableAdapter {
static class TestCellViewHolder extends AbstractViewHolder {
final LinearLayout cell_container;
final TextView cell_textview;
TestCellViewHolder(View itemView) {
super(itemView);
cell_container = itemView.findViewById(com.evrencoskun.tableview.test.R.id.cell_container);
cell_textview = itemView.findViewById(com.evrencoskun.tableview.test.R.id.cell_data);
}
}
@NonNull
public AbstractViewHolder onCreateCellViewHolder(@NonNull ViewGroup parent, int viewType) {
View layout = LayoutInflater.from(parent.getContext())
.inflate(R.layout.cell_layout, parent, false);
return new TestCellViewHolder(layout);
}
public void onBindCellViewHolder(@NonNull AbstractViewHolder holder, Cell cell, int
columnPosition, int rowPosition) {
TestCellViewHolder viewHolder = (TestCellViewHolder) holder;
viewHolder.cell_textview.setText(cell.getData() != null ? cell.getData().toString() : "");
viewHolder.cell_container.getLayoutParams().width = LinearLayout.LayoutParams.WRAP_CONTENT;
viewHolder.cell_textview.requestLayout();
}
@NonNull
public AbstractViewHolder onCreateColumnHeaderViewHolder(@NonNull ViewGroup parent, int
viewType) {
View layout = LayoutInflater.from(parent.getContext())
.inflate(R.layout.cell_layout, parent, false);
return new TestCellViewHolder(layout);
}
public void onBindColumnHeaderViewHolder(@NonNull AbstractViewHolder holder,
ColumnHeader columnHeader, int position) {
TestCellViewHolder viewHolder = (TestCellViewHolder) holder;
if (columnHeader.getData() != null)
viewHolder.cell_textview.setText(columnHeader.getData().toString());
viewHolder.cell_container.getLayoutParams().width = LinearLayout.LayoutParams.WRAP_CONTENT;
viewHolder.cell_textview.requestLayout();
}
@NonNull
public AbstractViewHolder onCreateRowHeaderViewHolder(@NonNull ViewGroup parent, int viewType) {
View layout = LayoutInflater.from(parent.getContext())
.inflate(R.layout.cell_layout, parent, false);
return new TestCellViewHolder(layout);
}
public void onBindRowHeaderViewHolder(@NonNull AbstractViewHolder holder,
RowHeader rowHeader, int position) {
TestCellViewHolder viewHolder = (TestCellViewHolder) holder;
if (rowHeader.getData() != null)
viewHolder.cell_textview.setText(rowHeader.getData().toString());
}
@NonNull
public View onCreateCornerView(@NonNull ViewGroup parent) {
return LayoutInflater.from(parent.getContext())
.inflate(R.layout.corner_layout, parent, false);
}
}
================================================
FILE: tableview/src/androidTest/java/com/evrencoskun/tableview/test/data/SimpleData.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Andrew Beck
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableview.test.data;
import com.evrencoskun.tableview.test.models.Cell;
import com.evrencoskun.tableview.test.models.ColumnHeader;
import com.evrencoskun.tableview.test.models.RowHeader;
import java.util.ArrayList;
import java.util.List;
public class SimpleData {
private List> cells;
private List columnHeaders;
private List rowHeaders;
public SimpleData(int size){
init(size, size);
}
public SimpleData(int columnSize, int rowSize) {
init(columnSize, rowSize);
}
private void init(int columnSize, int rowSize) {
rowHeaders = new ArrayList<>();
for (int i = 0; i < rowSize; i++) {
rowHeaders.add(new RowHeader(String.valueOf(i), "r:" + i));
}
columnHeaders = new ArrayList<>();
for (int i = 0; i < columnSize; i++) {
columnHeaders.add(new ColumnHeader(String.valueOf(i), "c:" + i));
}
cells = new ArrayList<>();
for (int i = 0; i < rowSize; i++) {
ArrayList cellList = new ArrayList<>();
for (int j = 0; j < columnSize; j++) {
String id = j + ":" + i;
cellList.add(new Cell(id, "r:" + i + "c:" + j));
}
cells.add(cellList);
}
}
public List> getCells() {
return cells;
}
public void setCells(List> cells) {
this.cells = cells;
}
public List getColumnHeaders() {
return columnHeaders;
}
public void setColumnHeaders(List columnHeaders) {
this.columnHeaders = columnHeaders;
}
public List getRowHeaders() {
return rowHeaders;
}
public void setRowHeaders(List rowHeaders) {
this.rowHeaders = rowHeaders;
}
}
================================================
FILE: tableview/src/androidTest/java/com/evrencoskun/tableview/test/matchers/ViewWidthMatcher.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Andrew Beck
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableview.test.matchers;
import android.view.View;
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
public class ViewWidthMatcher extends TypeSafeMatcher {
private final int expectedWidth;
public ViewWidthMatcher(int expectedWidth) {
super(View.class);
this.expectedWidth = expectedWidth;
}
@Override
protected boolean matchesSafely(View target) {
int targetWidth = target.getWidth();
return targetWidth == expectedWidth;
}
@Override
public void describeTo(Description description) {
description.appendText("with WidthMatcher: ");
description.appendValue(expectedWidth);
}
}
================================================
FILE: tableview/src/androidTest/java/com/evrencoskun/tableview/test/models/Cell.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Andrew Beck
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableview.test.models;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.evrencoskun.tableview.filter.IFilterableModel;
import com.evrencoskun.tableview.sort.ISortableModel;
public class Cell implements ISortableModel, IFilterableModel {
@NonNull
private String mId;
@Nullable
private Object mData;
@NonNull
private String mFilterKeyword;
public Cell(@NonNull String id, @Nullable Object data) {
this.mId = id;
this.mData = data;
this.mFilterKeyword = String.valueOf(data);
}
/**
* This is necessary for sorting process.
* See ISortableModel
*/
@NonNull
@Override
public String getId() {
return mId;
}
/**
* This is necessary for sorting process.
* See ISortableModel
*/
@Nullable
@Override
public Object getContent() {
return mData;
}
@Nullable
public Object getData() {
return mData;
}
public void setData(@Nullable Object data) {
mData = data;
}
@NonNull
@Override
public String getFilterableKeyword() {
return mFilterKeyword;
}
}
================================================
FILE: tableview/src/androidTest/java/com/evrencoskun/tableview/test/models/ColumnHeader.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Andrew Beck
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableview.test.models;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public class ColumnHeader extends Cell {
public ColumnHeader(@NonNull String id, @Nullable String data) {
super(id, data);
}
}
================================================
FILE: tableview/src/androidTest/java/com/evrencoskun/tableview/test/models/RowHeader.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Andrew Beck
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableview.test.models;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public class RowHeader extends Cell {
public RowHeader(@NonNull String id, @Nullable String data) {
super(id, data);
}
}
================================================
FILE: tableview/src/androidTest/res/layout/cell_layout.xml
================================================
================================================
FILE: tableview/src/androidTest/res/layout/column_layout.xml
================================================
================================================
FILE: tableview/src/androidTest/res/layout/corner_bottom_left.xml
================================================
================================================
FILE: tableview/src/androidTest/res/layout/corner_bottom_right.xml
================================================
================================================
FILE: tableview/src/androidTest/res/layout/corner_default.xml
================================================
================================================
FILE: tableview/src/androidTest/res/layout/corner_layout.xml
================================================
================================================
FILE: tableview/src/androidTest/res/layout/corner_top_left.xml
================================================
================================================
FILE: tableview/src/androidTest/res/layout/corner_top_right.xml
================================================
================================================
FILE: tableview/src/androidTest/res/layout/reverse_layout.xml
================================================
================================================
FILE: tableview/src/androidTest/res/layout/row_layout.xml
================================================
================================================
FILE: tableview/src/androidTest/res/values/colors.xml
================================================
#ffffff#0a0a0a#0a0a0a
================================================
FILE: tableview/src/androidTest/res/values/dimens.xml
================================================
40dp12sp55dp40dp
================================================
FILE: tableview/src/main/AndroidManifest.xml
================================================
================================================
FILE: tableview/src/main/java/com/evrencoskun/tableview/ITableView.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Evren Coşkun
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableview;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import com.evrencoskun.tableview.adapter.AbstractTableAdapter;
import com.evrencoskun.tableview.adapter.recyclerview.CellRecyclerView;
import com.evrencoskun.tableview.filter.Filter;
import com.evrencoskun.tableview.handler.ColumnSortHandler;
import com.evrencoskun.tableview.handler.FilterHandler;
import com.evrencoskun.tableview.handler.ScrollHandler;
import com.evrencoskun.tableview.handler.SelectionHandler;
import com.evrencoskun.tableview.handler.VisibilityHandler;
import com.evrencoskun.tableview.layoutmanager.CellLayoutManager;
import com.evrencoskun.tableview.layoutmanager.ColumnHeaderLayoutManager;
import com.evrencoskun.tableview.listener.ITableViewListener;
import com.evrencoskun.tableview.listener.scroll.HorizontalRecyclerViewListener;
import com.evrencoskun.tableview.listener.scroll.VerticalRecyclerViewListener;
import com.evrencoskun.tableview.sort.SortState;
/**
* Created by evrencoskun on 19/06/2017.
*/
public interface ITableView {
void addView(View child, ViewGroup.LayoutParams params);
boolean hasFixedWidth();
boolean isIgnoreSelectionColors();
boolean isShowHorizontalSeparators();
boolean isShowVerticalSeparators();
boolean isAllowClickInsideCell();
boolean isSortable();
@NonNull
Context getContext();
@NonNull
CellRecyclerView getCellRecyclerView();
@NonNull
CellRecyclerView getColumnHeaderRecyclerView();
@NonNull
CellRecyclerView getRowHeaderRecyclerView();
@NonNull
ColumnHeaderLayoutManager getColumnHeaderLayoutManager();
@NonNull
CellLayoutManager getCellLayoutManager();
@NonNull
LinearLayoutManager getRowHeaderLayoutManager();
@NonNull
HorizontalRecyclerViewListener getHorizontalRecyclerViewListener();
@NonNull
VerticalRecyclerViewListener getVerticalRecyclerViewListener();
@Nullable
ITableViewListener getTableViewListener();
@NonNull
SelectionHandler getSelectionHandler();
@Nullable
ColumnSortHandler getColumnSortHandler();
@NonNull
VisibilityHandler getVisibilityHandler();
@NonNull
DividerItemDecoration getHorizontalItemDecoration();
@NonNull
DividerItemDecoration getVerticalItemDecoration();
@NonNull
SortState getSortingStatus(int column);
@Nullable
SortState getRowHeaderSortingStatus();
void scrollToColumnPosition(int column);
void scrollToColumnPosition(int column, int offset);
void scrollToRowPosition(int row);
void scrollToRowPosition(int row, int offset);
void showRow(int row);
void hideRow(int row);
boolean isRowVisible(int row);
void showAllHiddenRows();
void clearHiddenRowList();
void showColumn(int column);
void hideColumn(int column);
boolean isColumnVisible(int column);
void showAllHiddenColumns();
void clearHiddenColumnList();
int getShadowColor();
int getSelectedColor();
int getUnSelectedColor();
int getSeparatorColor();
void sortColumn(int columnPosition, @NonNull SortState sortState);
void sortRowHeader(@NonNull SortState sortState);
void remeasureColumnWidth(int column);
int getRowHeaderWidth();
void setRowHeaderWidth(int rowHeaderWidth);
boolean getShowCornerView();
enum CornerViewLocation {
TOP_LEFT(0),
TOP_RIGHT(1),
BOTTOM_LEFT(2),
BOTTOM_RIGHT(3);
int id;
CornerViewLocation(int id) {
this.id = id;
}
static CornerViewLocation fromId(int id) {
for (CornerViewLocation c : values()) {
if (c.id == id) return c;
}
// If enum not found return default of Top Left
return TOP_LEFT;
}
}
CornerViewLocation getCornerViewLocation();
void setCornerViewLocation(CornerViewLocation cornerViewLocation);
int getGravity();
boolean getReverseLayout();
void setReverseLayout(boolean reverseLayout);
@Nullable
AbstractTableAdapter getAdapter();
/**
* Filters the whole table using the provided Filter object which supports multiple filters.
*
* @param filter The filter object.
*/
void filter(@NonNull Filter filter);
/**
* Retrieves the FilterHandler of the TableView.
*
* @return The FilterHandler of the TableView.
*/
@Nullable
FilterHandler getFilterHandler();
/**
* Retrieves the ScrollHandler of the TableView.
*
* @return The ScrollHandler of the TableView.
*/
@NonNull
ScrollHandler getScrollHandler();
}
================================================
FILE: tableview/src/main/java/com/evrencoskun/tableview/TableView.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Evren Coşkun
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableview;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import com.evrencoskun.tableview.adapter.AbstractTableAdapter;
import com.evrencoskun.tableview.adapter.recyclerview.CellRecyclerView;
import com.evrencoskun.tableview.adapter.recyclerview.holder.AbstractViewHolder;
import com.evrencoskun.tableview.filter.Filter;
import com.evrencoskun.tableview.handler.ColumnSortHandler;
import com.evrencoskun.tableview.handler.ColumnWidthHandler;
import com.evrencoskun.tableview.handler.FilterHandler;
import com.evrencoskun.tableview.handler.PreferencesHandler;
import com.evrencoskun.tableview.handler.ScrollHandler;
import com.evrencoskun.tableview.handler.SelectionHandler;
import com.evrencoskun.tableview.handler.VisibilityHandler;
import com.evrencoskun.tableview.layoutmanager.CellLayoutManager;
import com.evrencoskun.tableview.layoutmanager.ColumnHeaderLayoutManager;
import com.evrencoskun.tableview.listener.ITableViewListener;
import com.evrencoskun.tableview.listener.TableViewLayoutChangeListener;
import com.evrencoskun.tableview.listener.itemclick.ColumnHeaderRecyclerViewItemClickListener;
import com.evrencoskun.tableview.listener.itemclick.RowHeaderRecyclerViewItemClickListener;
import com.evrencoskun.tableview.listener.scroll.HorizontalRecyclerViewListener;
import com.evrencoskun.tableview.listener.scroll.VerticalRecyclerViewListener;
import com.evrencoskun.tableview.preference.SavedState;
import com.evrencoskun.tableview.sort.SortState;
import androidx.annotation.AttrRes;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
/**
* Created by evrencoskun on 11/06/2017.
*/
public class TableView extends FrameLayout implements ITableView {
@NonNull
protected CellRecyclerView mCellRecyclerView;
@NonNull
protected CellRecyclerView mColumnHeaderRecyclerView;
@NonNull
protected CellRecyclerView mRowHeaderRecyclerView;
@Nullable
protected AbstractTableAdapter mTableAdapter;
@Nullable
private ITableViewListener mTableViewListener;
@NonNull
private VerticalRecyclerViewListener mVerticalRecyclerListener;
@NonNull
private HorizontalRecyclerViewListener mHorizontalRecyclerViewListener;
@NonNull
private ColumnHeaderLayoutManager mColumnHeaderLayoutManager;
@NonNull
private LinearLayoutManager mRowHeaderLayoutManager;
@NonNull
private CellLayoutManager mCellLayoutManager;
@NonNull
private DividerItemDecoration mVerticalItemDecoration;
@NonNull
private DividerItemDecoration mHorizontalItemDecoration;
@NonNull
private SelectionHandler mSelectionHandler;
@Nullable
private ColumnSortHandler mColumnSortHandler;
@NonNull
private VisibilityHandler mVisibilityHandler;
@NonNull
private ScrollHandler mScrollHandler;
@Nullable
private FilterHandler mFilterHandler;
@NonNull
private PreferencesHandler mPreferencesHandler;
@NonNull
private ColumnWidthHandler mColumnWidthHandler;
private int mRowHeaderWidth;
private int mColumnHeaderHeight;
private int mSelectedColor;
private int mUnSelectedColor;
private int mShadowColor;
private int mSeparatorColor = -1;
private boolean mHasFixedWidth;
private boolean mIgnoreSelectionColors;
private boolean mShowHorizontalSeparators = true;
private boolean mShowVerticalSeparators = true;
private boolean mAllowClickInsideCell = false;
private boolean mAllowClickInsideRowHeader = false;
private boolean mAllowClickInsideColumnHeader = false;
private boolean mIsSortable;
private boolean mShowCornerView = false;
private CornerViewLocation mCornerViewLocation;
private boolean mReverseLayout = false;
public TableView(@NonNull Context context) {
super(context);
initialDefaultValues(null);
initialize();
}
public TableView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initialDefaultValues(attrs);
initialize();
}
public TableView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int
defStyleAttr) {
super(context, attrs, defStyleAttr);
initialDefaultValues(null);
initialize();
}
/**
* Two Part class construction
* Allows you to set various properties before class initialization if {@code intialize = false}
* Allowing more control when programmically creating the class
*
* @param context
* @param initialize {@code false} to not call second part of class construction
*
*
Note: If initialize is false you need to call {@code initilize()} method yourself.
*
*/
public TableView(@NonNull Context context, boolean initialize) {
super(context);
initialDefaultValues(null);
if (initialize) initialize();
}
private void initialDefaultValues(@Nullable AttributeSet attrs) {
// Dimensions
mRowHeaderWidth = (int) getResources().getDimension(R.dimen.default_row_header_width);
mColumnHeaderHeight = (int) getResources().getDimension(R.dimen
.default_column_header_height);
// Cornerview location
mCornerViewLocation = ITableView.CornerViewLocation.TOP_LEFT;
// Reverse Layout
mReverseLayout = false;
// Colors
mSelectedColor = ContextCompat.getColor(getContext(), R.color
.table_view_default_selected_background_color);
mUnSelectedColor = ContextCompat.getColor(getContext(), R.color
.table_view_default_unselected_background_color);
mShadowColor = ContextCompat.getColor(getContext(), R.color
.table_view_default_shadow_background_color);
if (attrs == null) {
// That means TableView is created programmatically.
return;
}
// Get values from xml attributes
TypedArray a = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable
.TableView, 0, 0);
try {
// Dimensions
mRowHeaderWidth = (int) a.getDimension(R.styleable.TableView_row_header_width,
mRowHeaderWidth);
mColumnHeaderHeight = (int) a.getDimension(R.styleable
.TableView_column_header_height, mColumnHeaderHeight);
// CornerView location
mCornerViewLocation = CornerViewLocation.fromId(a.getInt(R.styleable.TableView_corner_view_location, 0));
// Reverse Layout
mReverseLayout = a.getBoolean(R.styleable.TableView_reverse_layout, mReverseLayout);
// Colors
mSelectedColor = a.getColor(R.styleable.TableView_selected_color, mSelectedColor);
mUnSelectedColor = a.getColor(R.styleable.TableView_unselected_color, mUnSelectedColor);
mShadowColor = a.getColor(R.styleable.TableView_shadow_color, mShadowColor);
mSeparatorColor = a.getColor(R.styleable.TableView_separator_color, ContextCompat
.getColor(getContext(), R.color.table_view_default_separator_color));
// Booleans
mShowVerticalSeparators = a.getBoolean(R.styleable.TableView_show_vertical_separator,
mShowVerticalSeparators);
mShowHorizontalSeparators = a.getBoolean(R.styleable
.TableView_show_horizontal_separator, mShowHorizontalSeparators);
mAllowClickInsideCell = a.getBoolean(R.styleable.TableView_allow_click_inside_cell,
mAllowClickInsideCell);
mAllowClickInsideRowHeader = a.getBoolean(R.styleable.TableView_allow_click_inside_row_header,
mAllowClickInsideRowHeader);
mAllowClickInsideColumnHeader = a.getBoolean(R.styleable.TableView_allow_click_inside_column_header,
mAllowClickInsideColumnHeader);
} finally {
a.recycle();
}
}
/**
* Second Part of class construction
*
*
Note: This should only be called directly if the class was constructed
* with initialize boolean set to {@code false}
*/
public void initialize() {
// Create Views
mColumnHeaderRecyclerView = createColumnHeaderRecyclerView();
mRowHeaderRecyclerView = createRowHeaderRecyclerView();
mCellRecyclerView = createCellRecyclerView();
// Set some Id to help in identification
mColumnHeaderRecyclerView.setId(R.id.ColumnHeaderRecyclerView);
mRowHeaderRecyclerView.setId(R.id.RowHeaderRecyclerView);
mCellRecyclerView.setId(R.id.CellRecyclerView);
// Add Views
addView(mColumnHeaderRecyclerView);
addView(mRowHeaderRecyclerView);
addView(mCellRecyclerView);
// Create Handlers
mSelectionHandler = new SelectionHandler(this);
mVisibilityHandler = new VisibilityHandler(this);
mScrollHandler = new ScrollHandler(this);
mPreferencesHandler = new PreferencesHandler(this);
mColumnWidthHandler = new ColumnWidthHandler(this);
initializeListeners();
}
protected void initializeListeners() {
// --- Listeners to help Scroll synchronously ---
// It handles Vertical scroll listener
mVerticalRecyclerListener = new VerticalRecyclerViewListener(this);
// Set this listener both of Cell RecyclerView and RowHeader RecyclerView
mRowHeaderRecyclerView.addOnItemTouchListener(mVerticalRecyclerListener);
mCellRecyclerView.addOnItemTouchListener(mVerticalRecyclerListener);
// It handles Horizontal scroll listener
mHorizontalRecyclerViewListener = new HorizontalRecyclerViewListener(this);
// Set scroll listener to be able to scroll all rows synchrony.
mColumnHeaderRecyclerView.addOnItemTouchListener(mHorizontalRecyclerViewListener);
// --- Listeners to help item clicks ---
// Create item click listeners
// Add item click listener for column header recyclerView
if (mAllowClickInsideColumnHeader) {
ColumnHeaderRecyclerViewItemClickListener columnHeaderRecyclerViewItemClickListener = new ColumnHeaderRecyclerViewItemClickListener
(mColumnHeaderRecyclerView, this);
mColumnHeaderRecyclerView.addOnItemTouchListener(columnHeaderRecyclerViewItemClickListener);
}
// Add item click listener for row header recyclerView
if (mAllowClickInsideRowHeader) {
RowHeaderRecyclerViewItemClickListener rowHeaderRecyclerViewItemClickListener = new RowHeaderRecyclerViewItemClickListener
(mRowHeaderRecyclerView, this);
mRowHeaderRecyclerView.addOnItemTouchListener(rowHeaderRecyclerViewItemClickListener);
}
// Add Layout change listener both of Column Header & Cell recyclerView to detect
// changing size
// For some case, it is pretty necessary.
TableViewLayoutChangeListener layoutChangeListener = new TableViewLayoutChangeListener
(this);
mColumnHeaderRecyclerView.addOnLayoutChangeListener(layoutChangeListener);
mCellRecyclerView.addOnLayoutChangeListener(layoutChangeListener);
}
@NonNull
protected CellRecyclerView createColumnHeaderRecyclerView() {
CellRecyclerView recyclerView = new CellRecyclerView(getContext());
// Set layout manager
recyclerView.setLayoutManager(getColumnHeaderLayoutManager());
// Set layout params
LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT,
mColumnHeaderHeight, getGravity());
// If the corner is on the right the margin needs to be on the right
if (mCornerViewLocation == CornerViewLocation.TOP_RIGHT || mCornerViewLocation == CornerViewLocation.BOTTOM_RIGHT) {
layoutParams.rightMargin = mRowHeaderWidth;
} else {
layoutParams.leftMargin = mRowHeaderWidth;
}
recyclerView.setLayoutParams(layoutParams);
if (isShowHorizontalSeparators()) {
// Add vertical item decoration to display column line
recyclerView.addItemDecoration(getHorizontalItemDecoration());
}
return recyclerView;
}
@NonNull
protected CellRecyclerView createRowHeaderRecyclerView() {
CellRecyclerView recyclerView = new CellRecyclerView(getContext());
// Set layout manager
recyclerView.setLayoutManager(getRowHeaderLayoutManager());
// Set layout params
LayoutParams layoutParams = new LayoutParams(mRowHeaderWidth, LayoutParams.WRAP_CONTENT, getGravity());
// If the corner is on the bottom the margin needs to be on the bottom
if (mCornerViewLocation == CornerViewLocation.BOTTOM_LEFT || mCornerViewLocation == CornerViewLocation.BOTTOM_RIGHT) {
layoutParams.bottomMargin = mColumnHeaderHeight;
} else {
layoutParams.topMargin = mColumnHeaderHeight;
}
recyclerView.setLayoutParams(layoutParams);
if (isShowVerticalSeparators()) {
// Add vertical item decoration to display row line
recyclerView.addItemDecoration(getVerticalItemDecoration());
}
return recyclerView;
}
@NonNull
protected CellRecyclerView createCellRecyclerView() {
CellRecyclerView recyclerView = new CellRecyclerView(getContext());
// Disable multitouch
recyclerView.setMotionEventSplittingEnabled(false);
// Set layout manager
recyclerView.setLayoutManager(getCellLayoutManager());
// Set layout params
LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams
.WRAP_CONTENT, getGravity());
// If the corner is on the right the margin needs to be on the right
if (mCornerViewLocation == CornerViewLocation.TOP_RIGHT || mCornerViewLocation == CornerViewLocation.BOTTOM_RIGHT) {
layoutParams.rightMargin = mRowHeaderWidth;
} else {
layoutParams.leftMargin = mRowHeaderWidth;
}
// If the corner is on the bottom the margin needs to be on the bottom
if (mCornerViewLocation == CornerViewLocation.BOTTOM_LEFT || mCornerViewLocation == CornerViewLocation.BOTTOM_RIGHT) {
layoutParams.bottomMargin = mColumnHeaderHeight;
} else {
layoutParams.topMargin = mColumnHeaderHeight;
}
recyclerView.setLayoutParams(layoutParams);
if (isShowVerticalSeparators()) {
// Add vertical item decoration to display row line on center recycler view
recyclerView.addItemDecoration(getVerticalItemDecoration());
}
return recyclerView;
}
public void setAdapter(@Nullable AbstractTableAdapter tableAdapter) {
if (tableAdapter != null) {
this.mTableAdapter = tableAdapter;
this.mTableAdapter.setRowHeaderWidth(mRowHeaderWidth);
this.mTableAdapter.setColumnHeaderHeight(mColumnHeaderHeight);
this.mTableAdapter.setTableView(this);
// set adapters
mColumnHeaderRecyclerView.setAdapter(mTableAdapter.getColumnHeaderRecyclerViewAdapter());
mRowHeaderRecyclerView.setAdapter(mTableAdapter.getRowHeaderRecyclerViewAdapter());
mCellRecyclerView.setAdapter(mTableAdapter.getCellRecyclerViewAdapter());
// Create Sort Handler
mColumnSortHandler = new ColumnSortHandler(this);
// Create Filter Handler
mFilterHandler = new FilterHandler<>(this);
}
}
@Override
public boolean hasFixedWidth() {
return mHasFixedWidth;
}
public void setHasFixedWidth(boolean hasFixedWidth) {
this.mHasFixedWidth = hasFixedWidth;
// RecyclerView has also the same control to provide better performance.
mColumnHeaderRecyclerView.setHasFixedSize(hasFixedWidth);
}
@Override
public boolean isIgnoreSelectionColors() {
return mIgnoreSelectionColors;
}
public void setIgnoreSelectionColors(boolean ignoreSelectionColor) {
this.mIgnoreSelectionColors = ignoreSelectionColor;
}
@Override
public boolean isShowHorizontalSeparators() {
return mShowHorizontalSeparators;
}
@Override
public boolean isAllowClickInsideCell(){
return mAllowClickInsideCell;
}
@Override
public boolean isSortable() {
return mIsSortable;
}
public void setShowHorizontalSeparators(boolean showSeparators) {
this.mShowHorizontalSeparators = showSeparators;
}
@Override
public boolean isShowVerticalSeparators() {
return mShowVerticalSeparators;
}
public void setShowVerticalSeparators(boolean showSeparators) {
this.mShowVerticalSeparators = showSeparators;
}
@NonNull
@Override
public CellRecyclerView getCellRecyclerView() {
return mCellRecyclerView;
}
@NonNull
@Override
public CellRecyclerView getColumnHeaderRecyclerView() {
return mColumnHeaderRecyclerView;
}
@NonNull
@Override
public CellRecyclerView getRowHeaderRecyclerView() {
return mRowHeaderRecyclerView;
}
@NonNull
@Override
public ColumnHeaderLayoutManager getColumnHeaderLayoutManager() {
if (mColumnHeaderLayoutManager == null) {
mColumnHeaderLayoutManager = new ColumnHeaderLayoutManager(getContext(), this);
if (mReverseLayout) mColumnHeaderLayoutManager.setReverseLayout(true);
}
return mColumnHeaderLayoutManager;
}
@NonNull
@Override
public CellLayoutManager getCellLayoutManager() {
if (mCellLayoutManager == null) {
mCellLayoutManager = new CellLayoutManager(getContext(), this);
}
return mCellLayoutManager;
}
@NonNull
@Override
public LinearLayoutManager getRowHeaderLayoutManager() {
if (mRowHeaderLayoutManager == null) {
mRowHeaderLayoutManager = new LinearLayoutManager(getContext(), LinearLayoutManager
.VERTICAL, false);
}
return mRowHeaderLayoutManager;
}
@NonNull
@Override
public HorizontalRecyclerViewListener getHorizontalRecyclerViewListener() {
return mHorizontalRecyclerViewListener;
}
@NonNull
@Override
public VerticalRecyclerViewListener getVerticalRecyclerViewListener() {
return mVerticalRecyclerListener;
}
@Nullable
@Override
public ITableViewListener getTableViewListener() {
return mTableViewListener;
}
public void setTableViewListener(@Nullable ITableViewListener tableViewListener) {
this.mTableViewListener = tableViewListener;
}
@Override
public void sortColumn(int columnPosition, @NonNull SortState sortState) {
mIsSortable = true;
mColumnSortHandler.sort(columnPosition, sortState);
}
@Override
public void sortRowHeader(@NonNull SortState sortState) {
mIsSortable = true;
mColumnSortHandler.sortByRowHeader(sortState);
}
@Override
public void remeasureColumnWidth(int column) {
// Remove calculated width value to be ready for recalculation.
getColumnHeaderLayoutManager().removeCachedWidth(column);
// Recalculate of the width values of the columns
getCellLayoutManager().fitWidthSize(column, false);
}
@Nullable
@Override
public AbstractTableAdapter getAdapter() {
return mTableAdapter;
}
@Override
public void filter(@NonNull Filter filter) {
mFilterHandler.filter(filter);
}
@Nullable
@Override
public FilterHandler getFilterHandler() {
return mFilterHandler;
}
@NonNull
@Override
public SortState getSortingStatus(int column) {
return mColumnSortHandler.getSortingStatus(column);
}
@Nullable
@Override
public SortState getRowHeaderSortingStatus() {
return mColumnSortHandler.getRowHeaderSortingStatus();
}
@Override
public void scrollToColumnPosition(int column) {
mScrollHandler.scrollToColumnPosition(column);
}
@Override
public void scrollToColumnPosition(int column, int offset) {
mScrollHandler.scrollToColumnPosition(column, offset);
}
@Override
public void scrollToRowPosition(int row) {
mScrollHandler.scrollToRowPosition(row);
}
@Override
public void scrollToRowPosition(int row, int offset) {
mScrollHandler.scrollToRowPosition(row, offset);
}
@NonNull
public ScrollHandler getScrollHandler() {
return mScrollHandler;
}
@Override
public void showRow(int row) {
mVisibilityHandler.showRow(row);
}
@Override
public void hideRow(int row) {
mVisibilityHandler.hideRow(row);
}
@Override
public void showAllHiddenRows() {
mVisibilityHandler.showAllHiddenRows();
}
@Override
public void clearHiddenRowList() {
mVisibilityHandler.clearHideRowList();
}
@Override
public void showColumn(int column) {
mVisibilityHandler.showColumn(column);
}
@Override
public void hideColumn(int column) {
mVisibilityHandler.hideColumn(column);
}
@Override
public boolean isColumnVisible(int column) {
return mVisibilityHandler.isColumnVisible(column);
}
@Override
public void showAllHiddenColumns() {
mVisibilityHandler.showAllHiddenColumns();
}
@Override
public void clearHiddenColumnList() {
mVisibilityHandler.clearHideColumnList();
}
@Override
public boolean isRowVisible(int row) {
return mVisibilityHandler.isRowVisible(row);
}
/**
* Returns the index of the selected row, -1 if no row is selected.
*/
public int getSelectedRow() {
return mSelectionHandler.getSelectedRowPosition();
}
public void setSelectedRow(int row) {
// Find the row header view holder which is located on row position.
AbstractViewHolder rowViewHolder = (AbstractViewHolder) getRowHeaderRecyclerView()
.findViewHolderForAdapterPosition(row);
mSelectionHandler.setSelectedRowPosition(rowViewHolder, row);
}
/**
* Returns the index of the selected column, -1 if no column is selected.
*/
public int getSelectedColumn() {
return mSelectionHandler.getSelectedColumnPosition();
}
public void setSelectedColumn(int column) {
// Find the column view holder which is located on column position .
AbstractViewHolder columnViewHolder = (AbstractViewHolder) getColumnHeaderRecyclerView()
.findViewHolderForAdapterPosition(column);
mSelectionHandler.setSelectedColumnPosition(columnViewHolder, column);
}
public void setSelectedCell(int column, int row) {
// Find the cell view holder which is located on x,y (column,row) position.
AbstractViewHolder cellViewHolder = getCellLayoutManager().getCellViewHolder(column, row);
mSelectionHandler.setSelectedCellPositions(cellViewHolder, column, row);
}
@NonNull
@Override
public SelectionHandler getSelectionHandler() {
return mSelectionHandler;
}
@Nullable
@Override
public ColumnSortHandler getColumnSortHandler() {
return mColumnSortHandler;
}
@NonNull
@Override
public VisibilityHandler getVisibilityHandler() {
return mVisibilityHandler;
}
@NonNull
@Override
public DividerItemDecoration getHorizontalItemDecoration() {
if (mHorizontalItemDecoration == null) {
mHorizontalItemDecoration = createItemDecoration(DividerItemDecoration.HORIZONTAL);
}
return mHorizontalItemDecoration;
}
@NonNull
@Override
public DividerItemDecoration getVerticalItemDecoration() {
if (mVerticalItemDecoration == null) {
mVerticalItemDecoration = createItemDecoration(DividerItemDecoration.VERTICAL);
}
return mVerticalItemDecoration;
}
@NonNull
protected DividerItemDecoration createItemDecoration(int orientation) {
DividerItemDecoration itemDecoration = new DividerItemDecoration(getContext(), orientation);
Drawable divider = ContextCompat.getDrawable(getContext(), R.drawable.cell_line_divider);
if (divider == null) {
return itemDecoration;
}
// That means; There is a custom separator color from user.
if (mSeparatorColor != -1) {
// Change its color
divider.setColorFilter(mSeparatorColor, PorterDuff.Mode.SRC_ATOP);
}
itemDecoration.setDrawable(divider);
return itemDecoration;
}
/**
* This method helps to change default selected color programmatically.
*
* @param selectedColor It must be Color int.
*/
public void setSelectedColor(@ColorInt int selectedColor) {
this.mSelectedColor = selectedColor;
}
@ColorInt
@Override
public int getSelectedColor() {
return mSelectedColor;
}
public void setSeparatorColor(@ColorInt int mSeparatorColor) {
this.mSeparatorColor = mSeparatorColor;
}
@ColorInt
@Override
public int getSeparatorColor() {
return mSeparatorColor;
}
/**
* This method helps to change default unselected color programmatically.
*
* @param unSelectedColor It must be Color int.
*/
public void setUnSelectedColor(@ColorInt int unSelectedColor) {
this.mUnSelectedColor = unSelectedColor;
}
@ColorInt
@Override
public int getUnSelectedColor() {
return mUnSelectedColor;
}
public void setShadowColor(@ColorInt int shadowColor) {
this.mShadowColor = shadowColor;
}
@Override
public @ColorInt
int getShadowColor() {
return mShadowColor;
}
/**
* get row header width
*
* @return size in pixel
*/
@Override
public int getRowHeaderWidth() {
return mRowHeaderWidth;
}
/**
* set RowHeaderWidth
*
* @param rowHeaderWidth in pixel
*/
@Override
public void setRowHeaderWidth(int rowHeaderWidth) {
this.mRowHeaderWidth = rowHeaderWidth;
// Update RowHeader layout width
ViewGroup.LayoutParams layoutParamsRow = mRowHeaderRecyclerView.getLayoutParams();
layoutParamsRow.width = rowHeaderWidth;
mRowHeaderRecyclerView.setLayoutParams(layoutParamsRow);
mRowHeaderRecyclerView.requestLayout();
// Update ColumnHeader left margin
LayoutParams layoutParamsColumn = (LayoutParams) mColumnHeaderRecyclerView.getLayoutParams();
// If the corner is on the right the margin needs to be on the right
if (mCornerViewLocation == CornerViewLocation.TOP_RIGHT || mCornerViewLocation == CornerViewLocation.BOTTOM_RIGHT) {
layoutParamsColumn.rightMargin = rowHeaderWidth;
} else {
layoutParamsColumn.leftMargin = rowHeaderWidth;
}
mColumnHeaderRecyclerView.setLayoutParams(layoutParamsColumn);
mColumnHeaderRecyclerView.requestLayout();
// Update Cells left margin
LayoutParams layoutParamsCell = (LayoutParams) mCellRecyclerView.getLayoutParams();
if (mCornerViewLocation == CornerViewLocation.TOP_RIGHT || mCornerViewLocation == CornerViewLocation.BOTTOM_RIGHT) {
layoutParamsCell.rightMargin = rowHeaderWidth;
} else {
layoutParamsCell.leftMargin = rowHeaderWidth;
}
mCellRecyclerView.setLayoutParams(layoutParamsCell);
mCellRecyclerView.requestLayout();
if (getAdapter() != null) {
// update CornerView size
getAdapter().setRowHeaderWidth(rowHeaderWidth);
}
}
public void setColumnWidth(int columnPosition, int width) {
mColumnWidthHandler.setColumnWidth(columnPosition, width);
}
public void setShowCornerView(boolean showCornerView){
mShowCornerView = showCornerView;
}
public boolean getShowCornerView(){
return mShowCornerView;
}
public CornerViewLocation getCornerViewLocation() { return mCornerViewLocation; }
@Override
public void setCornerViewLocation(CornerViewLocation cornerViewLocation) {
mCornerViewLocation = cornerViewLocation;
}
public int getGravity() {
int gravity;
switch (mCornerViewLocation) {
case TOP_LEFT:
gravity = Gravity.TOP|Gravity.LEFT;
break;
case TOP_RIGHT:
gravity = Gravity.TOP|Gravity.RIGHT;
break;
case BOTTOM_LEFT:
gravity = Gravity.BOTTOM|Gravity.LEFT;
break;
case BOTTOM_RIGHT:
gravity = Gravity.BOTTOM|Gravity.RIGHT;
break;
default:
gravity = Gravity.TOP|Gravity.LEFT;
break;
}
return gravity;
}
public boolean getReverseLayout(){ return mReverseLayout;}
public void setReverseLayout(boolean reverseLayout) {
mReverseLayout = reverseLayout;
}
@Nullable
@Override
protected Parcelable onSaveInstanceState() {
SavedState state = new SavedState(super.onSaveInstanceState());
// Save all preferences of The TableView before the configuration changed.
state.preferences = mPreferencesHandler.savePreferences();
return state;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
if (!(state instanceof SavedState)) {
super.onRestoreInstanceState(state);
return;
}
SavedState savedState = (SavedState) state;
super.onRestoreInstanceState(savedState.getSuperState());
// Reload the preferences
mPreferencesHandler.loadPreferences(savedState.preferences);
}
}
================================================
FILE: tableview/src/main/java/com/evrencoskun/tableview/adapter/AbstractTableAdapter.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Evren Coşkun
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableview.adapter;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.evrencoskun.tableview.ITableView;
import com.evrencoskun.tableview.adapter.recyclerview.CellRecyclerViewAdapter;
import com.evrencoskun.tableview.adapter.recyclerview.ColumnHeaderRecyclerViewAdapter;
import com.evrencoskun.tableview.adapter.recyclerview.RowHeaderRecyclerViewAdapter;
import java.util.ArrayList;
import java.util.List;
/**
* Created by evrencoskun on 10/06/2017.
*/
public abstract class AbstractTableAdapter implements ITableAdapter {
private int mRowHeaderWidth;
private int mColumnHeaderHeight;
private ColumnHeaderRecyclerViewAdapter mColumnHeaderRecyclerViewAdapter;
private RowHeaderRecyclerViewAdapter mRowHeaderRecyclerViewAdapter;
private CellRecyclerViewAdapter mCellRecyclerViewAdapter;
private View mCornerView;
protected List mColumnHeaderItems;
protected List mRowHeaderItems;
protected List> mCellItems;
private ITableView mTableView;
private List> dataSetChangedListeners;
public void setTableView(@NonNull ITableView tableView) {
mTableView = tableView;
initialize();
}
private void initialize() {
Context context = mTableView.getContext();
// Create Column header RecyclerView Adapter
mColumnHeaderRecyclerViewAdapter = new ColumnHeaderRecyclerViewAdapter<>(context,
mColumnHeaderItems, this);
// Create Row Header RecyclerView Adapter
mRowHeaderRecyclerViewAdapter = new RowHeaderRecyclerViewAdapter<>(context,
mRowHeaderItems, this);
// Create Cell RecyclerView Adapter
mCellRecyclerViewAdapter = new CellRecyclerViewAdapter<>(context, mCellItems, mTableView);
}
public void setColumnHeaderItems(@Nullable List columnHeaderItems) {
if (columnHeaderItems == null) {
return;
}
mColumnHeaderItems = columnHeaderItems;
// Invalidate the cached widths for letting the view measure the cells width
// from scratch.
mTableView.getColumnHeaderLayoutManager().clearCachedWidths();
// Set the items to the adapter
mColumnHeaderRecyclerViewAdapter.setItems(mColumnHeaderItems);
dispatchColumnHeaderDataSetChangesToListeners(columnHeaderItems);
}
public void setRowHeaderItems(@Nullable List rowHeaderItems) {
if (rowHeaderItems == null) {
return;
}
mRowHeaderItems = rowHeaderItems;
// Set the items to the adapter
mRowHeaderRecyclerViewAdapter.setItems(mRowHeaderItems);
dispatchRowHeaderDataSetChangesToListeners(mRowHeaderItems);
}
public void setCellItems(@Nullable List> cellItems) {
if (cellItems == null) {
return;
}
mCellItems = cellItems;
// Invalidate the cached widths for letting the view measure the cells width
// from scratch.
mTableView.getCellLayoutManager().clearCachedWidths();
// Set the items to the adapter
mCellRecyclerViewAdapter.setItems(mCellItems);
dispatchCellDataSetChangesToListeners(mCellItems);
}
public void setAllItems(
@Nullable List columnHeaderItems,
@Nullable List rowHeaderItems,
@Nullable List> cellItems
) {
// Set all items
setColumnHeaderItems(columnHeaderItems);
setRowHeaderItems(rowHeaderItems);
setCellItems(cellItems);
// Control corner view
updateCornerViewState(columnHeaderItems, rowHeaderItems);
}
private void updateCornerViewState(
@Nullable List columnHeaderItems,
@Nullable List rowHeaderItems
) {
boolean hasColumnHeaders = columnHeaderItems != null && !columnHeaderItems.isEmpty();
boolean hasRowHeaders = rowHeaderItems != null && !rowHeaderItems.isEmpty();
boolean showCornerView = mTableView != null && mTableView.getShowCornerView();
boolean needCornerSpace = hasColumnHeaders && (hasRowHeaders || showCornerView);
// Create the corner view if we need it
if (mCornerView == null && needCornerSpace) {
// No TableView is associated with this Adapter, so we can't create the corner view
if (mTableView == null) {
return;
}
FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
mRowHeaderWidth,
mColumnHeaderHeight,
mTableView.getGravity()
);
// Create corner view
mCornerView = onCreateCornerView((ViewGroup) mTableView);
// Set the corner location
mTableView.addView(mCornerView, layoutParams);
}
// We don't have any corner view to update
if (mCornerView == null) {
return;
}
if (needCornerSpace) {
mCornerView.setVisibility(View.VISIBLE);
} else {
mCornerView.setVisibility(View.GONE);
}
}
@Override
public int getColumnHeaderItemViewType(int position) {
return 0;
}
@Override
public int getRowHeaderItemViewType(int position) {
return 0;
}
@Override
public int getCellItemViewType(int position) {
return 0;
}
@Nullable
@Override
public View getCornerView() {
return mCornerView;
}
public ColumnHeaderRecyclerViewAdapter getColumnHeaderRecyclerViewAdapter() {
return mColumnHeaderRecyclerViewAdapter;
}
public RowHeaderRecyclerViewAdapter getRowHeaderRecyclerViewAdapter() {
return mRowHeaderRecyclerViewAdapter;
}
public CellRecyclerViewAdapter getCellRecyclerViewAdapter() {
return mCellRecyclerViewAdapter;
}
public void setRowHeaderWidth(int rowHeaderWidth) {
this.mRowHeaderWidth = rowHeaderWidth;
if (mCornerView != null) {
ViewGroup.LayoutParams layoutParams = mCornerView.getLayoutParams();
layoutParams.width = rowHeaderWidth;
}
}
public void setColumnHeaderHeight(int columnHeaderHeight) {
this.mColumnHeaderHeight = columnHeaderHeight;
}
@Nullable
public CH getColumnHeaderItem(int position) {
if ((mColumnHeaderItems == null || mColumnHeaderItems.isEmpty()) || position < 0 ||
position >= mColumnHeaderItems.size()) {
return null;
}
return mColumnHeaderItems.get(position);
}
@Nullable
public RH getRowHeaderItem(int position) {
if ((mRowHeaderItems == null || mRowHeaderItems.isEmpty()) || position < 0 || position >=
mRowHeaderItems.size()) {
return null;
}
return mRowHeaderItems.get(position);
}
@Nullable
public C getCellItem(int columnPosition, int rowPosition) {
if ((mCellItems == null || mCellItems.isEmpty()) || columnPosition < 0 || rowPosition >=
mCellItems.size() || mCellItems.get(rowPosition) == null || rowPosition < 0 ||
columnPosition >= mCellItems.get(rowPosition).size()) {
return null;
}
return mCellItems.get(rowPosition).get(columnPosition);
}
@Nullable
public List getCellRowItems(int rowPosition) {
return (List) mCellRecyclerViewAdapter.getItem(rowPosition);
}
public void removeRow(int rowPosition) {
mCellRecyclerViewAdapter.deleteItem(rowPosition);
mRowHeaderRecyclerViewAdapter.deleteItem(rowPosition);
}
public void removeRow(int rowPosition, boolean updateRowHeader) {
mCellRecyclerViewAdapter.deleteItem(rowPosition);
// To be able update the row header data
if (updateRowHeader) {
rowPosition = mRowHeaderRecyclerViewAdapter.getItemCount() - 1;
// Cell RecyclerView items should be notified.
// Because, other items stores the old row position.
mCellRecyclerViewAdapter.notifyDataSetChanged();
}
mRowHeaderRecyclerViewAdapter.deleteItem(rowPosition);
}
public void removeRowRange(int rowPositionStart, int itemCount) {
mCellRecyclerViewAdapter.deleteItemRange(rowPositionStart, itemCount);
mRowHeaderRecyclerViewAdapter.deleteItemRange(rowPositionStart, itemCount);
}
public void removeRowRange(int rowPositionStart, int itemCount, boolean updateRowHeader) {
mCellRecyclerViewAdapter.deleteItemRange(rowPositionStart, itemCount);
// To be able update the row header data sets
if (updateRowHeader) {
rowPositionStart = mRowHeaderRecyclerViewAdapter.getItemCount() - 1 - itemCount;
// Cell RecyclerView items should be notified.
// Because, other items stores the old row position.
mCellRecyclerViewAdapter.notifyDataSetChanged();
}
mRowHeaderRecyclerViewAdapter.deleteItemRange(rowPositionStart, itemCount);
}
public void addRow(int rowPosition, @Nullable RH rowHeaderItem, @Nullable List cellItems) {
mCellRecyclerViewAdapter.addItem(rowPosition, cellItems);
mRowHeaderRecyclerViewAdapter.addItem(rowPosition, rowHeaderItem);
}
public void addRowRange(int rowPositionStart, @Nullable List rowHeaderItem, @Nullable List> cellItems) {
mRowHeaderRecyclerViewAdapter.addItemRange(rowPositionStart, rowHeaderItem);
mCellRecyclerViewAdapter.addItemRange(rowPositionStart, cellItems);
}
public void changeRowHeaderItem(int rowPosition, @Nullable RH rowHeaderModel) {
mRowHeaderRecyclerViewAdapter.changeItem(rowPosition, rowHeaderModel);
}
public void changeRowHeaderItemRange(int rowPositionStart, @Nullable List rowHeaderModelList) {
mRowHeaderRecyclerViewAdapter.changeItemRange(rowPositionStart, rowHeaderModelList);
}
public void changeCellItem(int columnPosition, int rowPosition, C cellModel) {
List cellItems = (List) mCellRecyclerViewAdapter.getItem(rowPosition);
if (cellItems != null && cellItems.size() > columnPosition) {
// Update cell row items.
cellItems.set(columnPosition, cellModel);
mCellRecyclerViewAdapter.changeItem(rowPosition, cellItems);
}
}
public void changeColumnHeader(int columnPosition, @Nullable CH columnHeaderModel) {
mColumnHeaderRecyclerViewAdapter.changeItem(columnPosition, columnHeaderModel);
}
public void changeColumnHeaderRange(int columnPositionStart, @Nullable List columnHeaderModelList) {
mColumnHeaderRecyclerViewAdapter.changeItemRange(columnPositionStart,
columnHeaderModelList);
}
@NonNull
public List getCellColumnItems(int columnPosition) {
return mCellRecyclerViewAdapter.getColumnItems(columnPosition);
}
public void removeColumn(int columnPosition) {
mColumnHeaderRecyclerViewAdapter.deleteItem(columnPosition);
mCellRecyclerViewAdapter.removeColumnItems(columnPosition);
}
public void addColumn(int columnPosition, @Nullable CH columnHeaderItem, @NonNull List cellItems) {
mColumnHeaderRecyclerViewAdapter.addItem(columnPosition, columnHeaderItem);
mCellRecyclerViewAdapter.addColumnItems(columnPosition, cellItems);
}
public final void notifyDataSetChanged() {
mColumnHeaderRecyclerViewAdapter.notifyDataSetChanged();
mRowHeaderRecyclerViewAdapter.notifyDataSetChanged();
mCellRecyclerViewAdapter.notifyCellDataSetChanged();
}
@Override
public ITableView getTableView() {
return mTableView;
}
private void dispatchColumnHeaderDataSetChangesToListeners(@NonNull List newColumnHeaderItems) {
if (dataSetChangedListeners != null) {
for (AdapterDataSetChangedListener listener : dataSetChangedListeners) {
listener.onColumnHeaderItemsChanged(newColumnHeaderItems);
}
}
}
private void dispatchRowHeaderDataSetChangesToListeners(@NonNull final List newRowHeaderItems) {
if (dataSetChangedListeners != null) {
for (AdapterDataSetChangedListener listener : dataSetChangedListeners) {
listener.onRowHeaderItemsChanged(newRowHeaderItems);
}
}
}
private void dispatchCellDataSetChangesToListeners(@NonNull List> newCellItems) {
if (dataSetChangedListeners != null) {
for (AdapterDataSetChangedListener listener : dataSetChangedListeners) {
listener.onCellItemsChanged(newCellItems);
}
}
}
/**
* Sets the listener for changes of data set on the TableView.
*
* @param listener The AdapterDataSetChangedListener listener.
*/
@Override
public void addAdapterDataSetChangedListener(@NonNull AdapterDataSetChangedListener listener) {
if (dataSetChangedListeners == null) {
dataSetChangedListeners = new ArrayList<>();
}
dataSetChangedListeners.add(listener);
}
}
================================================
FILE: tableview/src/main/java/com/evrencoskun/tableview/adapter/AdapterDataSetChangedListener.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Evren Coşkun
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableview.adapter;
import androidx.annotation.NonNull;
import java.util.List;
public abstract class AdapterDataSetChangedListener {
/**
* Dispatches changes on column header items to listener.
*
* @param columnHeaderItems The current column header items.
*/
public void onColumnHeaderItemsChanged(@NonNull List columnHeaderItems) {
}
/**
* Dispatches changes on row header items to listener.
*
* @param rowHeaderItems The current row header items.
*/
public void onRowHeaderItemsChanged(@NonNull List rowHeaderItems) {
}
/**
* Dispatches changes on cell items to listener.
*
* @param cellItems The current cell items.
*/
public void onCellItemsChanged(@NonNull List> cellItems) {
}
/**
* Dispatches the changes on column header, row header and cell items.
*
* @param columnHeaderItems The current column header items.
* @param rowHeaderItems The current row header items.
* @param cellItems The current cell items.
*/
public void onDataSetChanged(
List columnHeaderItems,
List rowHeaderItems,
List> cellItems) {
}
}
================================================
FILE: tableview/src/main/java/com/evrencoskun/tableview/adapter/ITableAdapter.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Evren Coşkun
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableview.adapter;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.evrencoskun.tableview.ITableView;
import com.evrencoskun.tableview.adapter.recyclerview.holder.AbstractViewHolder;
/**
* Created by evrencoskun on 10/06/2017.
*/
public interface ITableAdapter {
int getColumnHeaderItemViewType(int position);
int getRowHeaderItemViewType(int position);
int getCellItemViewType(int position);
View getCornerView();
@NonNull
AbstractViewHolder onCreateCellViewHolder(@NonNull ViewGroup parent, int viewType);
void onBindCellViewHolder(@NonNull AbstractViewHolder holder, @Nullable C cellItemModel, int columnPosition, int rowPosition);
@NonNull
AbstractViewHolder onCreateColumnHeaderViewHolder(@NonNull ViewGroup parent, int viewType);
void onBindColumnHeaderViewHolder(@NonNull AbstractViewHolder holder, @Nullable CH columnHeaderItemModel, int columnPosition);
@NonNull
AbstractViewHolder onCreateRowHeaderViewHolder(@NonNull ViewGroup parent, int viewType);
void onBindRowHeaderViewHolder(@NonNull AbstractViewHolder holder, @Nullable RH rowHeaderItemModel, int rowPosition);
@NonNull
View onCreateCornerView(@NonNull ViewGroup parent);
ITableView getTableView();
/**
* Sets the listener for changes of data set on the TableView.
*
* @param listener The AdapterDataSetChangedListener listener.
*/
void addAdapterDataSetChangedListener(@NonNull AdapterDataSetChangedListener listener);
}
================================================
FILE: tableview/src/main/java/com/evrencoskun/tableview/adapter/recyclerview/AbstractRecyclerViewAdapter.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Evren Coşkun
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableview.adapter.recyclerview;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import com.evrencoskun.tableview.adapter.recyclerview.holder.AbstractViewHolder;
import java.util.ArrayList;
import java.util.List;
/**
* Created by evrencoskun on 10/06/2017.
*/
public abstract class AbstractRecyclerViewAdapter extends RecyclerView
.Adapter {
@NonNull
protected List mItemList;
@NonNull
protected Context mContext;
public AbstractRecyclerViewAdapter(@NonNull Context context) {
this(context, null);
}
public AbstractRecyclerViewAdapter(@NonNull Context context, @Nullable List itemList) {
mContext = context;
if (itemList == null) {
mItemList = new ArrayList<>();
} else {
setItems(itemList);
}
}
@Override
public int getItemCount() {
return mItemList.size();
}
@NonNull
public List getItems() {
return mItemList;
}
public void setItems(@NonNull List itemList) {
mItemList = new ArrayList<>(itemList);
this.notifyDataSetChanged();
}
public void setItems(@NonNull List itemList, boolean notifyDataSet) {
mItemList = new ArrayList<>(itemList);
if (notifyDataSet) {
this.notifyDataSetChanged();
}
}
@Nullable
public T getItem(int position) {
if (mItemList.isEmpty() || position < 0 || position >= mItemList.size()) {
return null;
}
return mItemList.get(position);
}
public void deleteItem(int position) {
if (position != RecyclerView.NO_POSITION) {
mItemList.remove(position);
notifyItemRemoved(position);
}
}
public void deleteItemRange(int positionStart, int itemCount) {
for (int i = positionStart + itemCount - 1; i >= positionStart; i--) {
if (i != RecyclerView.NO_POSITION) {
mItemList.remove(i);
}
}
notifyItemRangeRemoved(positionStart, itemCount);
}
public void addItem(int position, @Nullable T item) {
if (position != RecyclerView.NO_POSITION && item != null) {
mItemList.add(position, item);
notifyItemInserted(position);
}
}
public void addItemRange(int positionStart, @Nullable List items) {
if (items != null) {
for (int i = 0; i < items.size(); i++) {
mItemList.add((i + positionStart), items.get(i));
}
notifyItemRangeInserted(positionStart, items.size());
}
}
public void changeItem(int position, @Nullable T item) {
if (position != RecyclerView.NO_POSITION && item != null) {
mItemList.set(position, item);
notifyItemChanged(position);
}
}
public void changeItemRange(int positionStart, @Nullable List items) {
if (items != null && mItemList.size() > positionStart + items.size()) {
for (int i = 0; i < items.size(); i++) {
mItemList.set(i + positionStart, items.get(i));
}
notifyItemRangeChanged(positionStart, items.size());
}
}
@Override
public int getItemViewType(int position) {
return 1;
}
}
================================================
FILE: tableview/src/main/java/com/evrencoskun/tableview/adapter/recyclerview/CellRecyclerView.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Evren Coşkun
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableview.adapter.recyclerview;
import android.content.Context;
import android.util.Log;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.evrencoskun.tableview.R;
import com.evrencoskun.tableview.listener.scroll.HorizontalRecyclerViewListener;
import com.evrencoskun.tableview.listener.scroll.VerticalRecyclerViewListener;
/**
* Created by evrencoskun on 19/06/2017.
*/
public class CellRecyclerView extends RecyclerView {
private static final String LOG_TAG = CellRecyclerView.class.getSimpleName();
private int mScrolledX = 0;
private int mScrolledY = 0;
private boolean mIsHorizontalScrollListenerRemoved = true;
private boolean mIsVerticalScrollListenerRemoved = true;
public CellRecyclerView(@NonNull Context context) {
super(context);
// These are necessary.
this.setHasFixedSize(false);
this.setNestedScrollingEnabled(false);
// These are for better scrolling process.
this.setItemViewCacheSize(context.getResources().getInteger(R.integer
.default_item_cache_size));
this.setDrawingCacheEnabled(true);
this.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
}
@Override
public void onScrolled(int dx, int dy) {
mScrolledX += dx;
mScrolledY += dy;
super.onScrolled(dx, dy);
}
public int getScrolledX() {
return mScrolledX;
}
public void clearScrolledX() {
mScrolledX = 0;
}
public int getScrolledY() {
return mScrolledY;
}
@Override
public void addOnScrollListener(@NonNull OnScrollListener listener) {
if (listener instanceof HorizontalRecyclerViewListener) {
if (mIsHorizontalScrollListenerRemoved) {
mIsHorizontalScrollListenerRemoved = false;
super.addOnScrollListener(listener);
} else {
// Do not let add the listener
Log.w(LOG_TAG, "mIsHorizontalScrollListenerRemoved has been tried to add itself "
+ "before remove the old one");
}
} else if (listener instanceof VerticalRecyclerViewListener) {
if (mIsVerticalScrollListenerRemoved) {
mIsVerticalScrollListenerRemoved = false;
super.addOnScrollListener(listener);
} else {
// Do not let add the listener
Log.w(LOG_TAG, "mIsVerticalScrollListenerRemoved has been tried to add itself " +
"before remove the old one");
}
} else {
super.addOnScrollListener(listener);
}
}
@Override
public void removeOnScrollListener(@NonNull OnScrollListener listener) {
if (listener instanceof HorizontalRecyclerViewListener) {
if (mIsHorizontalScrollListenerRemoved) {
// Do not let remove the listener
Log.e(LOG_TAG, "HorizontalRecyclerViewListener has been tried to remove " +
"itself before add new one");
} else {
mIsHorizontalScrollListenerRemoved = true;
super.removeOnScrollListener(listener);
}
} else if (listener instanceof VerticalRecyclerViewListener) {
if (mIsVerticalScrollListenerRemoved) {
// Do not let remove the listener
Log.e(LOG_TAG, "mIsVerticalScrollListenerRemoved has been tried to remove " +
"itself before add new one");
} else {
mIsVerticalScrollListenerRemoved = true;
super.removeOnScrollListener(listener);
}
} else {
super.removeOnScrollListener(listener);
}
}
public boolean isHorizontalScrollListenerRemoved() {
return mIsHorizontalScrollListenerRemoved;
}
public boolean isScrollOthers() {
return !mIsHorizontalScrollListenerRemoved;
}
/**
* Begin a standard fling with an initial velocity along each axis in pixels per second.
* If the velocity given is below the system-defined minimum this method will return false
* and no fling will occur.
*
* @param velocityX Initial horizontal velocity in pixels per second
* @param velocityY Initial vertical velocity in pixels per second
* @return true if the fling was started, false if the velocity was too low to fling or
* LayoutManager does not support scrolling in the axis fling is issued.
* @see LayoutManager#canScrollVertically()
* @see LayoutManager#canScrollHorizontally()
*/
@Override
public boolean fling(int velocityX, int velocityY) {
// Adjust speeds to be able to provide smoother scroll.
//velocityX *= 0.6;
//velocityY *= 0.6;
return super.fling(velocityX, velocityY);
}
}
================================================
FILE: tableview/src/main/java/com/evrencoskun/tableview/adapter/recyclerview/CellRecyclerViewAdapter.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Evren Coşkun
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableview.adapter.recyclerview;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import com.evrencoskun.tableview.ITableView;
import com.evrencoskun.tableview.adapter.recyclerview.holder.AbstractViewHolder;
import com.evrencoskun.tableview.adapter.recyclerview.holder.AbstractViewHolder.SelectionState;
import com.evrencoskun.tableview.handler.ScrollHandler;
import com.evrencoskun.tableview.handler.SelectionHandler;
import com.evrencoskun.tableview.layoutmanager.CellLayoutManager;
import com.evrencoskun.tableview.layoutmanager.ColumnLayoutManager;
import com.evrencoskun.tableview.listener.itemclick.CellRecyclerViewItemClickListener;
import java.util.ArrayList;
import java.util.List;
/**
* Created by evrencoskun on 10/06/2017.
*/
public class CellRecyclerViewAdapter extends AbstractRecyclerViewAdapter {
@NonNull
private final ITableView mTableView;
@NonNull
private final RecyclerView.RecycledViewPool mRecycledViewPool;
// This is for testing purpose
private int mRecyclerViewId = 0;
public CellRecyclerViewAdapter(@NonNull Context context, @Nullable List itemList, @NonNull ITableView tableView) {
super(context, itemList);
this.mTableView = tableView;
// Create view pool to share Views between multiple RecyclerViews.
mRecycledViewPool = new RecyclerView.RecycledViewPool();
//TODO set the right value.
//mRecycledViewPool.setMaxRecycledViews(0, 110);
}
@NonNull
@Override
public AbstractViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
// Create a RecyclerView as a Row of the CellRecyclerView
CellRecyclerView recyclerView = new CellRecyclerView(mContext);
// Use the same view pool
recyclerView.setRecycledViewPool(mRecycledViewPool);
if (mTableView.isShowHorizontalSeparators()) {
// Add divider
recyclerView.addItemDecoration(mTableView.getHorizontalItemDecoration());
}
// To get better performance for fixed size TableView
recyclerView.setHasFixedSize(mTableView.hasFixedWidth());
// set touch mHorizontalListener to scroll synchronously
recyclerView.addOnItemTouchListener(mTableView.getHorizontalRecyclerViewListener());
// Add Item click listener for cell views
if (mTableView.isAllowClickInsideCell()) {
recyclerView.addOnItemTouchListener(new CellRecyclerViewItemClickListener(recyclerView,
mTableView));
}
// Set the Column layout manager that helps the fit width of the cell and column header
// and it also helps to locate the scroll position of the horizontal recyclerView
// which is row recyclerView
ColumnLayoutManager mColumnLayoutManager = new ColumnLayoutManager(mContext, mTableView);
if (mTableView.getReverseLayout()) mColumnLayoutManager.setReverseLayout(true);
recyclerView.setLayoutManager(mColumnLayoutManager);
// Create CellRow adapter
recyclerView.setAdapter(new CellRowRecyclerViewAdapter<>(mContext, mTableView));
// This is for testing purpose to find out which recyclerView is displayed.
recyclerView.setId(mRecyclerViewId);
mRecyclerViewId++;
return new CellRowViewHolder(recyclerView);
}
@Override
public void onBindViewHolder(@NonNull AbstractViewHolder holder, int yPosition) {
CellRowViewHolder viewHolder = (CellRowViewHolder) holder;
CellRowRecyclerViewAdapter viewAdapter = (CellRowRecyclerViewAdapter) viewHolder
.recyclerView.getAdapter();
// Get the list
List rowList = (List) mItemList.get(yPosition);
// Set Row position
viewAdapter.setYPosition(yPosition);
// Set the list to the adapter
viewAdapter.setItems(rowList);
}
@Override
public void onViewAttachedToWindow(@NonNull AbstractViewHolder holder) {
super.onViewAttachedToWindow(holder);
CellRowViewHolder viewHolder = (CellRowViewHolder) holder;
ScrollHandler scrollHandler = mTableView.getScrollHandler();
// The below code helps to display a new attached recyclerView on exact scrolled position.
((ColumnLayoutManager) viewHolder.recyclerView.getLayoutManager())
.scrollToPositionWithOffset(scrollHandler.getColumnPosition(), scrollHandler
.getColumnPositionOffset());
SelectionHandler selectionHandler = mTableView.getSelectionHandler();
if (selectionHandler.isAnyColumnSelected()) {
AbstractViewHolder cellViewHolder = (AbstractViewHolder) viewHolder.recyclerView
.findViewHolderForAdapterPosition(selectionHandler.getSelectedColumnPosition());
if (cellViewHolder != null) {
// Control to ignore selection color
if (!mTableView.isIgnoreSelectionColors()) {
cellViewHolder.setBackgroundColor(mTableView.getSelectedColor());
}
cellViewHolder.setSelected(SelectionState.SELECTED);
}
} else if (selectionHandler.isRowSelected(holder.getBindingAdapterPosition())) {
selectionHandler.changeSelectionOfRecyclerView(viewHolder.recyclerView,
SelectionState.SELECTED, mTableView.getSelectedColor());
}
}
@Override
public void onViewDetachedFromWindow(@NonNull AbstractViewHolder holder) {
super.onViewDetachedFromWindow(holder);
// Clear selection status of the view holder
mTableView.getSelectionHandler().changeSelectionOfRecyclerView(((CellRowViewHolder)
holder).recyclerView, SelectionState.UNSELECTED, mTableView.getUnSelectedColor());
}
@Override
public void onViewRecycled(@NonNull AbstractViewHolder holder) {
super.onViewRecycled(holder);
CellRowViewHolder viewHolder = (CellRowViewHolder) holder;
// ScrolledX should be cleared at that time. Because we need to prepare each
// recyclerView
// at onViewAttachedToWindow process.
viewHolder.recyclerView.clearScrolledX();
}
static class CellRowViewHolder extends AbstractViewHolder {
final CellRecyclerView recyclerView;
CellRowViewHolder(@NonNull View itemView) {
super(itemView);
recyclerView = (CellRecyclerView) itemView;
}
}
public void notifyCellDataSetChanged() {
CellRecyclerView[] visibleRecyclerViews = mTableView.getCellLayoutManager()
.getVisibleCellRowRecyclerViews();
if (visibleRecyclerViews.length > 0) {
for (CellRecyclerView cellRowRecyclerView : visibleRecyclerViews) {
if (cellRowRecyclerView != null) {
RecyclerView.Adapter adapter = cellRowRecyclerView.getAdapter();
if (adapter != null) {
adapter.notifyDataSetChanged();
}
}
}
} else {
notifyDataSetChanged();
}
}
/**
* This method helps to get cell item model that is located on given column position.
*
* @param columnPosition
*/
@NonNull
public List getColumnItems(int columnPosition) {
List cellItems = new ArrayList<>();
for (int i = 0; i < mItemList.size(); i++) {
List rowList = (List) mItemList.get(i);
if (rowList.size() > columnPosition) {
cellItems.add(rowList.get(columnPosition));
}
}
return cellItems;
}
public void removeColumnItems(int column) {
// Firstly, remove columns from visible recyclerViews.
// To be able provide removing animation, we need to notify just for given column position.
CellRecyclerView[] visibleRecyclerViews = mTableView.getCellLayoutManager()
.getVisibleCellRowRecyclerViews();
for (CellRecyclerView cellRowRecyclerView : visibleRecyclerViews) {
if (cellRowRecyclerView != null) {
AbstractRecyclerViewAdapter adapter = (AbstractRecyclerViewAdapter) cellRowRecyclerView.getAdapter();
if (adapter != null) {
adapter.deleteItem(column);
}
}
}
// Lets change the model list silently
// Create a new list which the column is already removed.
List> cellItems = new ArrayList<>();
for (int i = 0; i < mItemList.size(); i++) {
List rowList = new ArrayList<>((List) mItemList.get(i));
if (rowList.size() > column) {
rowList.remove(column);
}
cellItems.add(rowList);
}
// Change data without notifying. Because we already did for visible recyclerViews.
setItems((List) cellItems, false);
}
public void addColumnItems(int column, @NonNull List cellColumnItems) {
// It should be same size with exist model list.
if (cellColumnItems.size() != mItemList.size() || cellColumnItems.contains(null)) {
return;
}
// Firstly, add columns from visible recyclerViews.
// To be able provide removing animation, we need to notify just for given column position.
CellLayoutManager layoutManager = mTableView.getCellLayoutManager();
for (int i = layoutManager.findFirstVisibleItemPosition(); i < layoutManager
.findLastVisibleItemPosition() + 1; i++) {
// Get the cell row recyclerView that is located on i position
RecyclerView cellRowRecyclerView = (RecyclerView) layoutManager.findViewByPosition(i);
// Add the item using its adapter.
((AbstractRecyclerViewAdapter) cellRowRecyclerView.getAdapter()).addItem(column,
cellColumnItems.get(i));
}
// Lets change the model list silently
List> cellItems = new ArrayList<>();
for (int i = 0; i < mItemList.size(); i++) {
List rowList = new ArrayList<>((List) mItemList.get(i));
if (rowList.size() > column) {
rowList.add(column, cellColumnItems.get(i));
}
cellItems.add(rowList);
}
// Change data without notifying. Because we already did for visible recyclerViews.
setItems((List) cellItems, false);
}
}
================================================
FILE: tableview/src/main/java/com/evrencoskun/tableview/adapter/recyclerview/CellRowRecyclerViewAdapter.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Evren Coşkun
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableview.adapter.recyclerview;
import android.content.Context;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import com.evrencoskun.tableview.ITableView;
import com.evrencoskun.tableview.adapter.ITableAdapter;
import com.evrencoskun.tableview.adapter.recyclerview.holder.AbstractViewHolder;
import com.evrencoskun.tableview.adapter.recyclerview.holder.AbstractViewHolder.SelectionState;
/**
* Created by evrencoskun on 10/06/2017.
*/
public class CellRowRecyclerViewAdapter extends AbstractRecyclerViewAdapter {
private int mYPosition;
private final ITableAdapter mTableAdapter;
@NonNull
private final ITableView mTableView;
public CellRowRecyclerViewAdapter(@NonNull Context context, @NonNull ITableView tableView) {
super(context, null);
this.mTableAdapter = tableView.getAdapter();
this.mTableView = tableView;
}
@NonNull
@Override
public AbstractViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return mTableAdapter.onCreateCellViewHolder(parent, viewType);
}
@Override
public void onBindViewHolder(@NonNull final AbstractViewHolder holder, final int xPosition) {
mTableAdapter.onBindCellViewHolder(holder, getItem(xPosition), xPosition, mYPosition);
}
public int getYPosition() {
return mYPosition;
}
public void setYPosition(int rowPosition) {
mYPosition = rowPosition;
}
@Override
public int getItemViewType(int position) {
return mTableAdapter.getCellItemViewType(position);
}
@Override
public void onViewAttachedToWindow(@NonNull AbstractViewHolder viewHolder) {
super.onViewAttachedToWindow(viewHolder);
SelectionState selectionState = mTableView.getSelectionHandler().getCellSelectionState
(viewHolder.getBindingAdapterPosition(), mYPosition);
// Control to ignore selection color
if (!mTableView.isIgnoreSelectionColors()) {
// Change the background color of the view considering selected row/cell position.
if (selectionState == SelectionState.SELECTED) {
viewHolder.setBackgroundColor(mTableView.getSelectedColor());
} else {
viewHolder.setBackgroundColor(mTableView.getUnSelectedColor());
}
}
// Change selection status
viewHolder.setSelected(selectionState);
}
@Override
public boolean onFailedToRecycleView(@NonNull AbstractViewHolder holder) {
return holder.onFailedToRecycleView();
}
@Override
public void onViewRecycled(@NonNull AbstractViewHolder holder) {
super.onViewRecycled(holder);
holder.onViewRecycled();
}
}
================================================
FILE: tableview/src/main/java/com/evrencoskun/tableview/adapter/recyclerview/ColumnHeaderRecyclerViewAdapter.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Evren Coşkun
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableview.adapter.recyclerview;
import android.content.Context;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.evrencoskun.tableview.ITableView;
import com.evrencoskun.tableview.adapter.ITableAdapter;
import com.evrencoskun.tableview.adapter.recyclerview.holder.AbstractSorterViewHolder;
import com.evrencoskun.tableview.adapter.recyclerview.holder.AbstractViewHolder;
import com.evrencoskun.tableview.adapter.recyclerview.holder.AbstractViewHolder.SelectionState;
import com.evrencoskun.tableview.sort.ColumnSortHelper;
import com.evrencoskun.tableview.sort.SortState;
import java.util.List;
/**
* Created by evrencoskun on 10/06/2017.
*/
public class ColumnHeaderRecyclerViewAdapter extends AbstractRecyclerViewAdapter {
@NonNull
private final ITableAdapter mTableAdapter;
private final ITableView mTableView;
private ColumnSortHelper mColumnSortHelper;
public ColumnHeaderRecyclerViewAdapter(@NonNull Context context, @Nullable List itemList, @NonNull ITableAdapter
tableAdapter) {
super(context, itemList);
this.mTableAdapter = tableAdapter;
this.mTableView = tableAdapter.getTableView();
}
@NonNull
@Override
public AbstractViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return mTableAdapter.onCreateColumnHeaderViewHolder(parent, viewType);
}
@Override
public void onBindViewHolder(@NonNull AbstractViewHolder holder, int position) {
mTableAdapter.onBindColumnHeaderViewHolder(holder, getItem(position), position);
}
@Override
public int getItemViewType(int position) {
return mTableAdapter.getColumnHeaderItemViewType(position);
}
@Override
public void onViewAttachedToWindow(@NonNull AbstractViewHolder viewHolder) {
super.onViewAttachedToWindow(viewHolder);
SelectionState selectionState = mTableView.getSelectionHandler().getColumnSelectionState
(viewHolder.getBindingAdapterPosition());
// Control to ignore selection color
if (!mTableView.isIgnoreSelectionColors()) {
// Change background color of the view considering it's selected state
mTableView.getSelectionHandler().changeColumnBackgroundColorBySelectionStatus
(viewHolder, selectionState);
}
// Change selection status
viewHolder.setSelected(selectionState);
// Control whether the TableView is sortable or not.
if (mTableView.isSortable()) {
if (viewHolder instanceof AbstractSorterViewHolder) {
// Get its sorting state
SortState state = getColumnSortHelper().getSortingStatus(viewHolder
.getBindingAdapterPosition());
// Fire onSortingStatusChanged
((AbstractSorterViewHolder) viewHolder).onSortingStatusChanged(state);
}
}
}
@NonNull
public ColumnSortHelper getColumnSortHelper() {
if (mColumnSortHelper == null) {
// It helps to store sorting state of column headers
this.mColumnSortHelper = new ColumnSortHelper(mTableView.getColumnHeaderLayoutManager
());
}
return mColumnSortHelper;
}
}
================================================
FILE: tableview/src/main/java/com/evrencoskun/tableview/adapter/recyclerview/RowHeaderRecyclerViewAdapter.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Evren Coşkun
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableview.adapter.recyclerview;
import android.content.Context;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.evrencoskun.tableview.ITableView;
import com.evrencoskun.tableview.adapter.ITableAdapter;
import com.evrencoskun.tableview.adapter.recyclerview.holder.AbstractViewHolder;
import com.evrencoskun.tableview.adapter.recyclerview.holder.AbstractViewHolder.SelectionState;
import com.evrencoskun.tableview.sort.RowHeaderSortHelper;
import java.util.List;
/**
* Created by evrencoskun on 10/06/2017.
*/
public class RowHeaderRecyclerViewAdapter extends AbstractRecyclerViewAdapter {
@NonNull
private final ITableAdapter mTableAdapter;
private final ITableView mTableView;
private RowHeaderSortHelper mRowHeaderSortHelper;
public RowHeaderRecyclerViewAdapter(@NonNull Context context, @Nullable List itemList, @NonNull ITableAdapter
tableAdapter) {
super(context, itemList);
this.mTableAdapter = tableAdapter;
this.mTableView = tableAdapter.getTableView();
}
@NonNull
@Override
public AbstractViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return mTableAdapter.onCreateRowHeaderViewHolder(parent, viewType);
}
@Override
public void onBindViewHolder(@NonNull AbstractViewHolder holder, int position) {
mTableAdapter.onBindRowHeaderViewHolder(holder, getItem(position), position);
}
@Override
public int getItemViewType(int position) {
return mTableAdapter.getRowHeaderItemViewType(position);
}
@Override
public void onViewAttachedToWindow(@NonNull AbstractViewHolder viewHolder) {
super.onViewAttachedToWindow(viewHolder);
SelectionState selectionState = mTableView.getSelectionHandler().getRowSelectionState
(viewHolder.getBindingAdapterPosition());
// Control to ignore selection color
if (!mTableView.isIgnoreSelectionColors()) {
// Change background color of the view considering it's selected state
mTableView.getSelectionHandler().changeRowBackgroundColorBySelectionStatus
(viewHolder, selectionState);
}
// Change selection status
viewHolder.setSelected(selectionState);
}
@NonNull
public RowHeaderSortHelper getRowHeaderSortHelper() {
if (mRowHeaderSortHelper == null) {
// It helps to store sorting state of row headers
this.mRowHeaderSortHelper = new RowHeaderSortHelper();
}
return mRowHeaderSortHelper;
}
}
================================================
FILE: tableview/src/main/java/com/evrencoskun/tableview/adapter/recyclerview/holder/AbstractSorterViewHolder.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Evren Coşkun
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableview.adapter.recyclerview.holder;
import android.view.View;
import androidx.annotation.NonNull;
import com.evrencoskun.tableview.sort.SortState;
/**
* Created by evrencoskun on 16.12.2017.
*/
public class AbstractSorterViewHolder extends AbstractViewHolder {
@NonNull
private SortState mSortState = SortState.UNSORTED;
public AbstractSorterViewHolder(@NonNull View itemView) {
super(itemView);
}
public void onSortingStatusChanged(@NonNull SortState pSortState) {
this.mSortState = pSortState;
}
@NonNull
public SortState getSortState() {
return mSortState;
}
}
================================================
FILE: tableview/src/main/java/com/evrencoskun/tableview/adapter/recyclerview/holder/AbstractViewHolder.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Evren Coşkun
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableview.adapter.recyclerview.holder;
import android.view.View;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
/**
* Created by evrencoskun on 23/10/2017.
*/
public abstract class AbstractViewHolder extends RecyclerView.ViewHolder {
public enum SelectionState {SELECTED, UNSELECTED, SHADOWED}
// Default value
@NonNull
private SelectionState m_eState = SelectionState.UNSELECTED;
public AbstractViewHolder(@NonNull View itemView) {
super(itemView);
}
public void setSelected(@NonNull SelectionState selectionState) {
m_eState = selectionState;
if (selectionState == SelectionState.SELECTED) {
itemView.setSelected(true);
} else if (selectionState == SelectionState.UNSELECTED) {
itemView.setSelected(false);
}
}
public boolean isSelected() {
return m_eState == SelectionState.SELECTED;
}
public boolean isShadowed() {
return m_eState == SelectionState.SHADOWED;
}
public void setBackgroundColor(@ColorInt int p_nColor) {
itemView.setBackgroundColor(p_nColor);
}
public void onViewRecycled() {
}
public boolean onFailedToRecycleView() {
return false;
}
}
================================================
FILE: tableview/src/main/java/com/evrencoskun/tableview/filter/Filter.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Evren Coşkun
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableview.filter;
import androidx.annotation.NonNull;
import com.evrencoskun.tableview.ITableView;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* Class used to store multiple filters for the TableView filtering feature.
*/
public class Filter {
/**
* List of filter items to be used for filtering.
*/
@NonNull
private final List filterItems;
/**
* The TableView instance used in this scope.
*/
@NonNull
private final ITableView tableView;
/**
* @param tableView The TableView to be filtered.
*/
public Filter(@NonNull ITableView tableView) {
this.tableView = tableView;
this.filterItems = new ArrayList<>();
}
/**
* Adds new filter item to the list. This should be used strictly once
* for filtering the whole table.
*
* @param filter the filter string.
*/
public void set(@NonNull String filter) {
set(-1, filter);
}
/**
* Adds new filter item to the list. The filter will be used on the
* specified column.
*
* @param column the target column for filtering.
* @param filter the filter string.
*/
public void set(int column, @NonNull String filter) {
final FilterItem filterItem = new FilterItem(
column == -1 ? FilterType.ALL : FilterType.COLUMN,
column,
filter
);
if (isAlreadyFiltering(column, filterItem)) {
if (filter.isEmpty()) {
remove(column, filterItem);
} else {
update(column, filterItem);
}
} else if (!filter.isEmpty()) {
add(filterItem);
}
}
/**
* Adds new filter item to the list of this class.
*
* @param filterItem The filter item to be added to the list.
*/
private void add(@NonNull FilterItem filterItem) {
filterItems.add(filterItem);
tableView.filter(this);
}
/**
* Removes a filter item from the list of this class.
*
* @param column The column to be checked for removing the filter item.
* @param filterItem The filter item to be removed.
*/
private void remove(int column, @NonNull FilterItem filterItem) {
// This would remove a FilterItem from the Filter list when the filter is cleared.
// Used Iterator for removing instead of canonical loop to prevent ConcurrentModificationException.
for (Iterator filterItemIterator = filterItems.iterator(); filterItemIterator.hasNext(); ) {
final FilterItem item = filterItemIterator.next();
if (column == -1 && item.getFilterType().equals(filterItem.getFilterType())) {
filterItemIterator.remove();
break;
} else if (item.getColumn() == filterItem.getColumn()) {
filterItemIterator.remove();
break;
}
}
tableView.filter(this);
}
/**
* Updates a filter item from the list of this class.
*
* @param column The column in which filter item will be updated.
* @param filterItem The updated filter item.
*/
private void update(int column, @NonNull FilterItem filterItem) {
// This would update an existing FilterItem from the Filter list.
// Used Iterator for updating instead of canonical loop to prevent ConcurrentModificationException.
for (Iterator filterItemIterator = filterItems.iterator(); filterItemIterator.hasNext(); ) {
final FilterItem item = filterItemIterator.next();
if (column == -1 && item.getFilterType().equals(filterItem.getFilterType())) {
filterItems.set(filterItems.indexOf(item), filterItem);
break;
} else if (item.getColumn() == filterItem.getColumn()) {
filterItems.set(filterItems.indexOf(item), filterItem);
break;
}
}
tableView.filter(this);
}
/**
* Method to check if a filter item is already added based on the column to be filtered.
*
* @param column The column to be checked if the list is already filtering.
* @param filterItem The filter item to be checked.
* @return True if a filter item for a specific column or for ALL is already in the list.
*/
private boolean isAlreadyFiltering(int column, @NonNull FilterItem filterItem) {
// This would determine if Filter is already filtering ALL or a specified COLUMN.
for (FilterItem item : filterItems) {
if (column == -1 && item.getFilterType().equals(filterItem.getFilterType())) {
return true;
} else if (item.getColumn() == filterItem.getColumn()) {
return true;
}
}
return false;
}
/**
* Returns the list of filter items.
*
* @return The list of filter items.
*/
@NonNull
public List getFilterItems() {
return this.filterItems;
}
}
================================================
FILE: tableview/src/main/java/com/evrencoskun/tableview/filter/FilterChangedListener.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Evren Coşkun
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableview.filter;
import androidx.annotation.NonNull;
import java.util.List;
public abstract class FilterChangedListener {
/**
* Called when a filter has been changed.
*
* @param filteredCellItems The list of filtered cell items.
* @param filteredRowHeaderItems The list of filtered row items.
*/
public void onFilterChanged(@NonNull List> filteredCellItems, @NonNull List filteredRowHeaderItems) {
}
/**
* Called when the TableView filters are cleared.
*
* @param originalCellItems The unfiltered cell item list.
* @param originalRowHeaderItems The unfiltered row header list.
*/
public void onFilterCleared(@NonNull List> originalCellItems, @NonNull List originalRowHeaderItems) {
}
}
================================================
FILE: tableview/src/main/java/com/evrencoskun/tableview/filter/FilterItem.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Evren Coşkun
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableview.filter;
import androidx.annotation.NonNull;
public class FilterItem {
@NonNull
private final FilterType filterType;
@NonNull
private final String filter;
private final int column;
public FilterItem(@NonNull FilterType type, int column, @NonNull String filter) {
this.filterType = type;
this.column = column;
this.filter = filter;
}
@NonNull
public FilterType getFilterType() {
return filterType;
}
@NonNull
public String getFilter() {
return filter;
}
public int getColumn() {
return column;
}
}
================================================
FILE: tableview/src/main/java/com/evrencoskun/tableview/filter/FilterType.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Evren Coşkun
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableview.filter;
/**
* The type of filter based on the target.
*/
public enum FilterType {
/**
* Filters only a specific column.
*/
COLUMN,
/**
* Filters all the data in the TableView.
*/
ALL
}
================================================
FILE: tableview/src/main/java/com/evrencoskun/tableview/filter/IFilterableModel.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Evren Coşkun
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableview.filter;
import androidx.annotation.NonNull;
public interface IFilterableModel {
/**
* Filterable query string for this object.
*
* @return query string for this object to be used in filtering.
*/
@NonNull
String getFilterableKeyword();
}
================================================
FILE: tableview/src/main/java/com/evrencoskun/tableview/handler/ColumnSortHandler.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Evren Coşkun
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableview.handler;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.DiffUtil;
import com.evrencoskun.tableview.ITableView;
import com.evrencoskun.tableview.adapter.recyclerview.CellRecyclerViewAdapter;
import com.evrencoskun.tableview.adapter.recyclerview.ColumnHeaderRecyclerViewAdapter;
import com.evrencoskun.tableview.adapter.recyclerview.RowHeaderRecyclerViewAdapter;
import com.evrencoskun.tableview.sort.ColumnForRowHeaderSortComparator;
import com.evrencoskun.tableview.sort.ColumnSortCallback;
import com.evrencoskun.tableview.sort.ColumnSortComparator;
import com.evrencoskun.tableview.sort.ColumnSortStateChangedListener;
import com.evrencoskun.tableview.sort.ISortableModel;
import com.evrencoskun.tableview.sort.RowHeaderForCellSortComparator;
import com.evrencoskun.tableview.sort.RowHeaderSortCallback;
import com.evrencoskun.tableview.sort.RowHeaderSortComparator;
import com.evrencoskun.tableview.sort.SortState;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Created by evrencoskun on 24.11.2017.
*/
public class ColumnSortHandler {
private final CellRecyclerViewAdapter> mCellRecyclerViewAdapter;
private final RowHeaderRecyclerViewAdapter mRowHeaderRecyclerViewAdapter;
private final ColumnHeaderRecyclerViewAdapter mColumnHeaderRecyclerViewAdapter;
private List columnSortStateChangedListeners = new ArrayList<>();
private boolean mEnableAnimation = true;
public boolean isEnableAnimation() {
return mEnableAnimation;
}
public void setEnableAnimation(boolean mEnableAnimation) {
this.mEnableAnimation = mEnableAnimation;
}
public ColumnSortHandler(@NonNull ITableView tableView) {
this.mCellRecyclerViewAdapter = (CellRecyclerViewAdapter>) tableView.getCellRecyclerView()
.getAdapter();
this.mRowHeaderRecyclerViewAdapter = (RowHeaderRecyclerViewAdapter) tableView
.getRowHeaderRecyclerView().getAdapter();
this.mColumnHeaderRecyclerViewAdapter = (ColumnHeaderRecyclerViewAdapter) tableView
.getColumnHeaderRecyclerView().getAdapter();
}
public void sortByRowHeader(@NonNull final SortState sortState) {
List originalRowHeaderList = mRowHeaderRecyclerViewAdapter.getItems();
List sortedRowHeaderList = new ArrayList<>(originalRowHeaderList);
List> originalList = mCellRecyclerViewAdapter.getItems();
List> sortedList = new ArrayList<>(originalList);
if (sortState != SortState.UNSORTED) {
// Do descending / ascending sort
Collections.sort(sortedRowHeaderList, new RowHeaderSortComparator(sortState));
// Sorting Columns/Cells using the same logic has sorting DataSet
RowHeaderForCellSortComparator rowHeaderForCellSortComparator
= new RowHeaderForCellSortComparator(
originalRowHeaderList,
originalList,
sortState);
Collections.sort(sortedList, rowHeaderForCellSortComparator);
}
mRowHeaderRecyclerViewAdapter.getRowHeaderSortHelper().setSortingStatus(sortState);
// Set sorted data list
swapItems(originalRowHeaderList, sortedRowHeaderList, sortedList, sortState);
}
public void sort(int column, @NonNull SortState sortState) {
List> originalList = mCellRecyclerViewAdapter.getItems();
List> sortedList = new ArrayList<>(originalList);
List originalRowHeaderList
= mRowHeaderRecyclerViewAdapter.getItems();
List sortedRowHeaderList
= new ArrayList<>(originalRowHeaderList);
if (sortState != SortState.UNSORTED) {
// Do descending / ascending sort
Collections.sort(sortedList, new ColumnSortComparator(column, sortState));
// Sorting RowHeader using the same logic has sorting DataSet
ColumnForRowHeaderSortComparator columnForRowHeaderSortComparator
= new ColumnForRowHeaderSortComparator(
originalRowHeaderList,
originalList,
column,
sortState);
Collections.sort(sortedRowHeaderList, columnForRowHeaderSortComparator);
}
// Update sorting list of column headers
mColumnHeaderRecyclerViewAdapter.getColumnSortHelper().setSortingStatus(column, sortState);
// Set sorted data list
swapItems(originalList, sortedList, column, sortedRowHeaderList, sortState);
}
private void swapItems(@NonNull List oldRowHeader,
@NonNull List newRowHeader,
@NonNull List> newColumnItems,
@NonNull SortState sortState
) {
// Set new items without calling notifyCellDataSetChanged method of CellRecyclerViewAdapter
mRowHeaderRecyclerViewAdapter.setItems(newRowHeader, !mEnableAnimation);
mCellRecyclerViewAdapter.setItems(newColumnItems, !mEnableAnimation);
if (mEnableAnimation) {
// Find the differences between old cell items and new items.
final RowHeaderSortCallback diffCallback = new RowHeaderSortCallback(oldRowHeader, newRowHeader);
final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(diffCallback);
diffResult.dispatchUpdatesTo(mRowHeaderRecyclerViewAdapter);
diffResult.dispatchUpdatesTo(mCellRecyclerViewAdapter);
}
for (ColumnSortStateChangedListener listener : columnSortStateChangedListeners) {
listener.onRowHeaderSortStatusChanged(sortState);
}
}
private void swapItems(@NonNull List> oldItems, @NonNull List>
newItems, int column, @NonNull List newRowHeader, @NonNull SortState sortState) {
// Set new items without calling notifyCellDataSetChanged method of CellRecyclerViewAdapter
mCellRecyclerViewAdapter.setItems(newItems, !mEnableAnimation);
mRowHeaderRecyclerViewAdapter.setItems(newRowHeader, !mEnableAnimation);
if (mEnableAnimation) {
// Find the differences between old cell items and new items.
final ColumnSortCallback diffCallback = new ColumnSortCallback(oldItems, newItems, column);
final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(diffCallback);
diffResult.dispatchUpdatesTo(mCellRecyclerViewAdapter);
diffResult.dispatchUpdatesTo(mRowHeaderRecyclerViewAdapter);
}
for (ColumnSortStateChangedListener listener : columnSortStateChangedListeners) {
listener.onColumnSortStatusChanged(column, sortState);
}
}
public void swapItems(@NonNull List> newItems, int column) {
List> oldItems = mCellRecyclerViewAdapter.getItems();
// Set new items without calling notifyCellDataSetChanged method of CellRecyclerViewAdapter
mCellRecyclerViewAdapter.setItems(newItems, !mEnableAnimation);
if (mEnableAnimation) {
// Find the differences between old cell items and new items.
final ColumnSortCallback diffCallback = new ColumnSortCallback(oldItems, newItems, column);
final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(diffCallback);
diffResult.dispatchUpdatesTo(mCellRecyclerViewAdapter);
diffResult.dispatchUpdatesTo(mRowHeaderRecyclerViewAdapter);
}
}
@NonNull
public SortState getSortingStatus(int column) {
return mColumnHeaderRecyclerViewAdapter.getColumnSortHelper().getSortingStatus(column);
}
@Nullable
public SortState getRowHeaderSortingStatus() {
return mRowHeaderRecyclerViewAdapter.getRowHeaderSortHelper().getSortingStatus();
}
/**
* Sets the listener for the changes in column sorting.
*
* @param listener ColumnSortStateChangedListener listener.
*/
public void addColumnSortStateChangedListener(@NonNull ColumnSortStateChangedListener listener) {
if (columnSortStateChangedListeners == null) {
columnSortStateChangedListeners = new ArrayList<>();
}
columnSortStateChangedListeners.add(listener);
}
}
================================================
FILE: tableview/src/main/java/com/evrencoskun/tableview/handler/ColumnWidthHandler.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Evren Coşkun
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableview.handler;
import androidx.annotation.NonNull;
import com.evrencoskun.tableview.ITableView;
/**
* Created by evrencoskun on 25.04.2018.
*/
public class ColumnWidthHandler {
@NonNull
private final ITableView mTableView;
public ColumnWidthHandler(@NonNull ITableView tableView) {
mTableView = tableView;
}
public void setColumnWidth(int columnPosition, int width) {
// Firstly set the column header cache map
mTableView.getColumnHeaderLayoutManager().setCacheWidth(columnPosition, width);
// Set each of cell items that is located on the column position
mTableView.getCellLayoutManager().setCacheWidth(columnPosition, width);
}
}
================================================
FILE: tableview/src/main/java/com/evrencoskun/tableview/handler/FilterHandler.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Evren Coşkun
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableview.handler;
import androidx.annotation.NonNull;
import com.evrencoskun.tableview.ITableView;
import com.evrencoskun.tableview.adapter.AdapterDataSetChangedListener;
import com.evrencoskun.tableview.adapter.recyclerview.CellRecyclerViewAdapter;
import com.evrencoskun.tableview.adapter.recyclerview.RowHeaderRecyclerViewAdapter;
import com.evrencoskun.tableview.filter.Filter;
import com.evrencoskun.tableview.filter.FilterChangedListener;
import com.evrencoskun.tableview.filter.FilterItem;
import com.evrencoskun.tableview.filter.FilterType;
import com.evrencoskun.tableview.filter.IFilterableModel;
import java.util.ArrayList;
import java.util.List;
public class FilterHandler {
private final CellRecyclerViewAdapter> mCellRecyclerViewAdapter;
private final RowHeaderRecyclerViewAdapter mRowHeaderRecyclerViewAdapter;
private List> originalCellDataStore;
private List originalRowDataStore;
private List> filterChangedListeners;
public FilterHandler(@NonNull ITableView tableView) {
tableView.getAdapter().addAdapterDataSetChangedListener(adapterDataSetChangedListener);
this.mCellRecyclerViewAdapter = (CellRecyclerViewAdapter>) tableView
.getCellRecyclerView().getAdapter();
this.mRowHeaderRecyclerViewAdapter = (RowHeaderRecyclerViewAdapter) tableView
.getRowHeaderRecyclerView().getAdapter();
}
public void filter(@NonNull Filter filter) {
if (originalCellDataStore == null || originalRowDataStore == null) {
return;
}
List> originalCellData = new ArrayList<>(originalCellDataStore);
List originalRowData = new ArrayList<>(originalRowDataStore);
List> filteredCellList = new ArrayList<>();
List filteredRowList = new ArrayList<>();
if (filter.getFilterItems().isEmpty()) {
filteredCellList = new ArrayList<>(originalCellDataStore);
filteredRowList = new ArrayList<>(originalRowDataStore);
dispatchFilterClearedToListeners(originalCellDataStore, originalRowDataStore);
} else {
for (int x = 0; x < filter.getFilterItems().size(); ) {
final FilterItem filterItem = filter.getFilterItems().get(x);
if (filterItem.getFilterType().equals(FilterType.ALL)) {
for (List itemsList : originalCellData) {
for (T item : itemsList) {
if (item
.getFilterableKeyword()
.toLowerCase()
.contains(filterItem
.getFilter()
.toLowerCase())) {
filteredCellList.add(itemsList);
filteredRowList.add(originalRowData.get(filteredCellList.indexOf(itemsList)));
break;
}
}
}
} else {
for (List itemsList : originalCellData) {
if (itemsList
.get(filterItem
.getColumn())
.getFilterableKeyword()
.toLowerCase()
.contains(filterItem
.getFilter()
.toLowerCase())) {
filteredCellList.add(itemsList);
filteredRowList.add(originalRowData.get(filteredCellList.indexOf(itemsList)));
}
}
}
// If this is the last filter to be processed, the filtered lists will not be cleared.
if (++x < filter.getFilterItems().size()) {
originalCellData = new ArrayList<>(filteredCellList);
originalRowData = new ArrayList<>(filteredRowList);
filteredCellList.clear();
filteredRowList.clear();
}
}
}
// Sets the filtered data to the TableView.
mRowHeaderRecyclerViewAdapter.setItems(filteredRowList, true);
mCellRecyclerViewAdapter.setItems(filteredCellList, true);
// Tells the listeners that the TableView is filtered.
dispatchFilterChangedToListeners(filteredCellList, filteredRowList);
}
@NonNull
@SuppressWarnings("unchecked")
private final AdapterDataSetChangedListener adapterDataSetChangedListener =
new AdapterDataSetChangedListener() {
@Override
public void onRowHeaderItemsChanged(@NonNull List rowHeaderItems) {
originalRowDataStore = new ArrayList<>(rowHeaderItems);
}
@Override
public void onCellItemsChanged(@NonNull List cellItems) {
originalCellDataStore = new ArrayList<>(cellItems);
}
};
private void dispatchFilterChangedToListeners(
@NonNull List> filteredCellItems,
@NonNull List filteredRowHeaderItems
) {
if (filterChangedListeners != null) {
for (FilterChangedListener listener : filterChangedListeners) {
listener.onFilterChanged(filteredCellItems, filteredRowHeaderItems);
}
}
}
private void dispatchFilterClearedToListeners(
@NonNull List> originalCellItems,
@NonNull List originalRowHeaderItems
) {
if (filterChangedListeners != null) {
for (FilterChangedListener listener : filterChangedListeners) {
listener.onFilterCleared(originalCellItems, originalRowHeaderItems);
}
}
}
public void addFilterChangedListener(@NonNull FilterChangedListener listener) {
if (filterChangedListeners == null) {
filterChangedListeners = new ArrayList<>();
}
filterChangedListeners.add(listener);
}
}
================================================
FILE: tableview/src/main/java/com/evrencoskun/tableview/handler/PreferencesHandler.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Evren Coşkun
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableview.handler;
import androidx.annotation.NonNull;
import com.evrencoskun.tableview.TableView;
import com.evrencoskun.tableview.preference.Preferences;
/**
* Created by evrencoskun on 3.03.2018.
*/
public class PreferencesHandler {
@NonNull
private final ScrollHandler scrollHandler;
@NonNull
private final SelectionHandler selectionHandler;
public PreferencesHandler(@NonNull TableView tableView) {
this.scrollHandler = tableView.getScrollHandler();
this.selectionHandler = tableView.getSelectionHandler();
}
@NonNull
public Preferences savePreferences() {
Preferences preferences = new Preferences();
preferences.columnPosition = scrollHandler.getColumnPosition();
preferences.columnPositionOffset = scrollHandler.getColumnPositionOffset();
preferences.rowPosition = scrollHandler.getRowPosition();
preferences.rowPositionOffset = scrollHandler.getRowPositionOffset();
preferences.selectedColumnPosition = selectionHandler.getSelectedColumnPosition();
preferences.selectedRowPosition = selectionHandler.getSelectedRowPosition();
return preferences;
}
public void loadPreferences(@NonNull Preferences preferences) {
scrollHandler.scrollToColumnPosition(preferences.columnPosition, preferences.columnPositionOffset);
scrollHandler.scrollToRowPosition(preferences.rowPosition, preferences.rowPositionOffset);
selectionHandler.setSelectedColumnPosition(preferences.selectedColumnPosition);
selectionHandler.setSelectedRowPosition(preferences.selectedRowPosition);
}
}
================================================
FILE: tableview/src/main/java/com/evrencoskun/tableview/handler/ScrollHandler.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Evren Coşkun
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableview.handler;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.evrencoskun.tableview.ITableView;
import com.evrencoskun.tableview.layoutmanager.CellLayoutManager;
import com.evrencoskun.tableview.layoutmanager.ColumnHeaderLayoutManager;
import com.evrencoskun.tableview.layoutmanager.ColumnLayoutManager;
/**
* Created by evrencoskun on 13.01.2018.
*/
public class ScrollHandler {
@NonNull
private final ITableView mTableView;
@NonNull
private final CellLayoutManager mCellLayoutManager;
@NonNull
private final LinearLayoutManager mRowHeaderLayoutManager;
@NonNull
private final ColumnHeaderLayoutManager mColumnHeaderLayoutManager;
public ScrollHandler(@NonNull ITableView tableView) {
this.mTableView = tableView;
this.mCellLayoutManager = tableView.getCellLayoutManager();
this.mRowHeaderLayoutManager = tableView.getRowHeaderLayoutManager();
this.mColumnHeaderLayoutManager = tableView.getColumnHeaderLayoutManager();
}
public void scrollToColumnPosition(int columnPosition) {
// TableView is not on screen yet.
if (!((View) mTableView).isShown()) {
// Change default value of the listener
mTableView.getHorizontalRecyclerViewListener().setScrollPosition(columnPosition);
}
// Column Header should be scrolled firstly because of fitting column width process.
scrollColumnHeader(columnPosition, 0);
scrollCellHorizontally(columnPosition, 0);
}
public void scrollToColumnPosition(int columnPosition, int offset) {
// TableView is not on screen yet.
if (!((View) mTableView).isShown()) {
// Change default value of the listener
mTableView.getHorizontalRecyclerViewListener().setScrollPosition(columnPosition);
mTableView.getHorizontalRecyclerViewListener().setScrollPositionOffset(offset);
}
// Column Header should be scrolled firstly because of fitting column width process.
scrollColumnHeader(columnPosition, offset);
scrollCellHorizontally(columnPosition, offset);
}
public void scrollToRowPosition(int rowPosition) {
mRowHeaderLayoutManager.scrollToPosition(rowPosition);
mCellLayoutManager.scrollToPosition(rowPosition);
}
public void scrollToRowPosition(int rowPosition, int offset) {
mRowHeaderLayoutManager.scrollToPositionWithOffset(rowPosition, offset);
mCellLayoutManager.scrollToPositionWithOffset(rowPosition, offset);
}
private void scrollCellHorizontally(int columnPosition, int offset) {
CellLayoutManager cellLayoutManager = mTableView.getCellLayoutManager();
for (int i = cellLayoutManager.findFirstVisibleItemPosition(); i < cellLayoutManager
.findLastVisibleItemPosition() + 1; i++) {
RecyclerView cellRowRecyclerView = (RecyclerView) cellLayoutManager
.findViewByPosition(i);
if (cellRowRecyclerView != null) {
ColumnLayoutManager columnLayoutManager = (ColumnLayoutManager)
cellRowRecyclerView.getLayoutManager();
columnLayoutManager.scrollToPositionWithOffset(columnPosition, offset);
}
}
}
private void scrollColumnHeader(int columnPosition, int offset) {
mTableView.getColumnHeaderLayoutManager().scrollToPositionWithOffset(columnPosition,
offset);
}
public int getColumnPosition() {
return mColumnHeaderLayoutManager.findFirstVisibleItemPosition();
}
public int getColumnPositionOffset() {
View child = mColumnHeaderLayoutManager.findViewByPosition(mColumnHeaderLayoutManager
.findFirstVisibleItemPosition());
if (child != null) {
return child.getLeft();
}
return 0;
}
public int getRowPosition() {
return mRowHeaderLayoutManager.findFirstVisibleItemPosition();
}
public int getRowPositionOffset() {
View child = mRowHeaderLayoutManager.findViewByPosition(mRowHeaderLayoutManager
.findFirstVisibleItemPosition());
if (child != null) {
return child.getLeft();
}
return 0;
}
}
================================================
FILE: tableview/src/main/java/com/evrencoskun/tableview/handler/SelectionHandler.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Evren Coşkun
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableview.handler;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.LinearLayoutManager;
import com.evrencoskun.tableview.ITableView;
import com.evrencoskun.tableview.adapter.recyclerview.CellRecyclerView;
import com.evrencoskun.tableview.adapter.recyclerview.holder.AbstractViewHolder;
import com.evrencoskun.tableview.adapter.recyclerview.holder.AbstractViewHolder.SelectionState;
import com.evrencoskun.tableview.layoutmanager.CellLayoutManager;
/**
* Created by evrencoskun on 24/10/2017.
*/
public class SelectionHandler {
public static final int UNSELECTED_POSITION = -1;
private int mSelectedRowPosition = UNSELECTED_POSITION;
private int mSelectedColumnPosition = UNSELECTED_POSITION;
private boolean shadowEnabled = true;
@NonNull
private final ITableView mTableView;
private AbstractViewHolder mPreviousSelectedViewHolder;
@NonNull
private final CellRecyclerView mColumnHeaderRecyclerView;
@NonNull
private final CellRecyclerView mRowHeaderRecyclerView;
@NonNull
private final CellLayoutManager mCellLayoutManager;
public SelectionHandler(@NonNull ITableView tableView) {
this.mTableView = tableView;
this.mColumnHeaderRecyclerView = mTableView.getColumnHeaderRecyclerView();
this.mRowHeaderRecyclerView = mTableView.getRowHeaderRecyclerView();
this.mCellLayoutManager = mTableView.getCellLayoutManager();
}
public boolean isShadowEnabled() {
return shadowEnabled;
}
public void setShadowEnabled(boolean shadowEnabled) {
this.shadowEnabled = shadowEnabled;
}
public void setSelectedCellPositions(@Nullable AbstractViewHolder selectedViewHolder, int column, int
row) {
this.setPreviousSelectedView(selectedViewHolder);
this.mSelectedColumnPosition = column;
this.mSelectedRowPosition = row;
if (shadowEnabled) {
selectedCellView();
}
}
public void setSelectedColumnPosition(@Nullable AbstractViewHolder selectedViewHolder, int column) {
this.setPreviousSelectedView(selectedViewHolder);
this.mSelectedColumnPosition = column;
selectedColumnHeader();
// Set unselected others
mSelectedRowPosition = UNSELECTED_POSITION;
}
public int getSelectedColumnPosition() {
return mSelectedColumnPosition;
}
public void setSelectedRowPosition(@Nullable AbstractViewHolder selectedViewHolder, int row) {
this.setPreviousSelectedView(selectedViewHolder);
this.mSelectedRowPosition = row;
selectedRowHeader();
// Set unselected others
mSelectedColumnPosition = UNSELECTED_POSITION;
}
public int getSelectedRowPosition() {
return mSelectedRowPosition;
}
public void setPreviousSelectedView(@Nullable AbstractViewHolder viewHolder) {
restorePreviousSelectedView();
if (mPreviousSelectedViewHolder != null) {
// Change color
mPreviousSelectedViewHolder.setBackgroundColor(mTableView.getUnSelectedColor());
// Change state
mPreviousSelectedViewHolder.setSelected(SelectionState.UNSELECTED);
}
AbstractViewHolder oldViewHolder = mCellLayoutManager.getCellViewHolder
(getSelectedColumnPosition(), getSelectedRowPosition());
if (oldViewHolder != null) {
// Change color
oldViewHolder.setBackgroundColor(mTableView.getUnSelectedColor());
// Change state
oldViewHolder.setSelected(SelectionState.UNSELECTED);
}
this.mPreviousSelectedViewHolder = viewHolder;
// Change color
mPreviousSelectedViewHolder.setBackgroundColor(mTableView.getSelectedColor());
// Change state
mPreviousSelectedViewHolder.setSelected(SelectionState.SELECTED);
}
private void restorePreviousSelectedView() {
if (mSelectedColumnPosition != UNSELECTED_POSITION && mSelectedRowPosition !=
UNSELECTED_POSITION) {
unselectedCellView();
} else if (mSelectedColumnPosition != UNSELECTED_POSITION) {
unselectedColumnHeader();
} else if (mSelectedRowPosition != UNSELECTED_POSITION) {
unselectedRowHeader();
}
}
private void selectedRowHeader() {
// Change background color of the selected cell views
changeVisibleCellViewsBackgroundForRow(mSelectedRowPosition, true);
// Change background color of the column headers to be shown as a shadow.
if (shadowEnabled) {
changeSelectionOfRecyclerView(mColumnHeaderRecyclerView, SelectionState.SHADOWED,
mTableView.getShadowColor());
}
}
private void unselectedRowHeader() {
changeVisibleCellViewsBackgroundForRow(mSelectedRowPosition, false);
// Change background color of the column headers to be shown as a normal.
changeSelectionOfRecyclerView(mColumnHeaderRecyclerView, SelectionState.UNSELECTED,
mTableView.getUnSelectedColor());
}
private void selectedCellView() {
int shadowColor = mTableView.getShadowColor();
// Change background color of the row header which is located on Y Position of the cell
// view.
AbstractViewHolder rowHeader = (AbstractViewHolder) mRowHeaderRecyclerView
.findViewHolderForAdapterPosition(mSelectedRowPosition);
// If view is null, that means the row view holder was already recycled.
if (rowHeader != null) {
// Change color
rowHeader.setBackgroundColor(shadowColor);
// Change state
rowHeader.setSelected(SelectionState.SHADOWED);
}
// Change background color of the column header which is located on X Position of the cell
// view.
AbstractViewHolder columnHeader = (AbstractViewHolder) mColumnHeaderRecyclerView
.findViewHolderForAdapterPosition(mSelectedColumnPosition);
if (columnHeader != null) {
// Change color
columnHeader.setBackgroundColor(shadowColor);
// Change state
columnHeader.setSelected(SelectionState.SHADOWED);
}
}
private void unselectedCellView() {
int unSelectedColor = mTableView.getUnSelectedColor();
// Change background color of the row header which is located on Y Position of the cell
// view.
AbstractViewHolder rowHeader = (AbstractViewHolder) mRowHeaderRecyclerView
.findViewHolderForAdapterPosition(mSelectedRowPosition);
// If view is null, that means the row view holder was already recycled.
if (rowHeader != null) {
// Change color
rowHeader.setBackgroundColor(unSelectedColor);
// Change state
rowHeader.setSelected(SelectionState.UNSELECTED);
}
// Change background color of the column header which is located on X Position of the cell
// view.
AbstractViewHolder columnHeader = (AbstractViewHolder) mColumnHeaderRecyclerView
.findViewHolderForAdapterPosition(mSelectedColumnPosition);
if (columnHeader != null) {
// Change color
columnHeader.setBackgroundColor(unSelectedColor);
// Change state
columnHeader.setSelected(SelectionState.UNSELECTED);
}
}
private void selectedColumnHeader() {
changeVisibleCellViewsBackgroundForColumn(mSelectedColumnPosition, true);
changeSelectionOfRecyclerView(mRowHeaderRecyclerView, SelectionState.SHADOWED, mTableView
.getShadowColor());
}
private void unselectedColumnHeader() {
changeVisibleCellViewsBackgroundForColumn(mSelectedColumnPosition, false);
changeSelectionOfRecyclerView(mRowHeaderRecyclerView, SelectionState.UNSELECTED,
mTableView.getUnSelectedColor());
}
public boolean isCellSelected(int column, int row) {
return (getSelectedColumnPosition() == column && getSelectedRowPosition() == row) ||
isColumnSelected(column) || isRowSelected(row);
}
@NonNull
public SelectionState getCellSelectionState(int column, int row) {
if (isCellSelected(column, row)) {
return SelectionState.SELECTED;
}
return SelectionState.UNSELECTED;
}
public boolean isColumnSelected(int column) {
return (getSelectedColumnPosition() == column && getSelectedRowPosition() ==
UNSELECTED_POSITION);
}
public boolean isColumnShadowed(int column) {
return (getSelectedColumnPosition() == column && getSelectedRowPosition() !=
UNSELECTED_POSITION) || (getSelectedColumnPosition() == UNSELECTED_POSITION &&
getSelectedRowPosition() != UNSELECTED_POSITION);
}
public boolean isAnyColumnSelected() {
return (getSelectedColumnPosition() != SelectionHandler.UNSELECTED_POSITION &&
getSelectedRowPosition() == SelectionHandler.UNSELECTED_POSITION);
}
@NonNull
public SelectionState getColumnSelectionState(int column) {
if (isColumnShadowed(column)) {
return SelectionState.SHADOWED;
} else if (isColumnSelected(column)) {
return SelectionState.SELECTED;
} else {
return SelectionState.UNSELECTED;
}
}
public boolean isRowSelected(int row) {
return (getSelectedRowPosition() == row && getSelectedColumnPosition() ==
UNSELECTED_POSITION);
}
public boolean isRowShadowed(int row) {
return (getSelectedRowPosition() == row && getSelectedColumnPosition() !=
UNSELECTED_POSITION) || (getSelectedRowPosition() == UNSELECTED_POSITION &&
getSelectedColumnPosition() != UNSELECTED_POSITION);
}
@NonNull
public SelectionState getRowSelectionState(int row) {
if (isRowShadowed(row)) {
return SelectionState.SHADOWED;
} else if (isRowSelected(row)) {
return SelectionState.SELECTED;
} else {
return SelectionState.UNSELECTED;
}
}
private void changeVisibleCellViewsBackgroundForRow(int row, boolean isSelected) {
int backgroundColor = mTableView.getUnSelectedColor();
SelectionState selectionState = SelectionState.UNSELECTED;
if (isSelected) {
backgroundColor = mTableView.getSelectedColor();
selectionState = SelectionState.SELECTED;
}
CellRecyclerView recyclerView = (CellRecyclerView) mCellLayoutManager.findViewByPosition
(row);
if (recyclerView == null) {
return;
}
changeSelectionOfRecyclerView(recyclerView, selectionState, backgroundColor);
}
private void changeVisibleCellViewsBackgroundForColumn(int column, boolean isSelected) {
int backgroundColor = mTableView.getUnSelectedColor();
SelectionState selectionState = SelectionState.UNSELECTED;
if (isSelected) {
backgroundColor = mTableView.getSelectedColor();
selectionState = SelectionState.SELECTED;
}
// Get visible Cell ViewHolders by Column Position
for (int i = mCellLayoutManager.findFirstVisibleItemPosition(); i < mCellLayoutManager
.findLastVisibleItemPosition() + 1; i++) {
CellRecyclerView cellRowRecyclerView = (CellRecyclerView) mCellLayoutManager
.findViewByPosition(i);
AbstractViewHolder holder = (AbstractViewHolder) cellRowRecyclerView
.findViewHolderForAdapterPosition(column);
if (holder != null) {
// Get each view container of the cell view and set unselected color.
holder.setBackgroundColor(backgroundColor);
// Change selection status of the view holder
holder.setSelected(selectionState);
}
}
}
public void changeRowBackgroundColorBySelectionStatus(@NonNull AbstractViewHolder viewHolder,
@NonNull SelectionState selectionState) {
if (shadowEnabled && selectionState == SelectionState.SHADOWED) {
viewHolder.setBackgroundColor(mTableView.getShadowColor());
} else if (selectionState == SelectionState.SELECTED) {
viewHolder.setBackgroundColor(mTableView.getSelectedColor());
} else {
viewHolder.setBackgroundColor(mTableView.getUnSelectedColor());
}
}
public void changeColumnBackgroundColorBySelectionStatus(@NonNull AbstractViewHolder viewHolder,
@NonNull SelectionState selectionState) {
if (shadowEnabled && selectionState == SelectionState.SHADOWED) {
viewHolder.setBackgroundColor(mTableView.getShadowColor());
} else if (selectionState == SelectionState.SELECTED) {
viewHolder.setBackgroundColor(mTableView.getSelectedColor());
} else {
viewHolder.setBackgroundColor(mTableView.getUnSelectedColor());
}
}
public void changeSelectionOfRecyclerView(CellRecyclerView recyclerView, @NonNull AbstractViewHolder
.SelectionState selectionState, @ColorInt int backgroundColor) {
LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView
.getLayoutManager();
for (int i = linearLayoutManager.findFirstVisibleItemPosition(); i < linearLayoutManager
.findLastVisibleItemPosition() + 1; i++) {
AbstractViewHolder viewHolder = (AbstractViewHolder) recyclerView
.findViewHolderForAdapterPosition(i);
if (viewHolder != null) {
if (!mTableView.isIgnoreSelectionColors()) {
// Change background color
viewHolder.setBackgroundColor(backgroundColor);
}
// Change selection status of the view holder
viewHolder.setSelected(selectionState);
}
}
}
public void clearSelection() {
unselectedRowHeader();
unselectedCellView();
unselectedColumnHeader();
}
public void setSelectedRowPosition(int row) {
this.mSelectedRowPosition = row;
}
public void setSelectedColumnPosition(int column) {
this.mSelectedColumnPosition = column;
}
}
================================================
FILE: tableview/src/main/java/com/evrencoskun/tableview/handler/VisibilityHandler.java
================================================
/*
* MIT License
*
* Copyright (c) 2021 Evren Coşkun
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.evrencoskun.tableview.handler;
import android.util.Log;
import android.util.SparseArray;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.evrencoskun.tableview.ITableView;
import com.evrencoskun.tableview.adapter.AbstractTableAdapter;
import java.util.List;
/**
* Created by evrencoskun on 24.12.2017.
*/
public class VisibilityHandler {
private static final String LOG_TAG = VisibilityHandler.class.getSimpleName();
@NonNull
private final ITableView mTableView;
@NonNull
private SparseArray mHideRowList = new SparseArray<>();
@NonNull
private SparseArray mHideColumnList = new SparseArray<>();
public VisibilityHandler(@NonNull ITableView tableView) {
this.mTableView = tableView;
}
public void hideRow(int row) {
int viewRow = convertIndexToViewIndex(row, mHideRowList);
if (mHideRowList.get(row) == null) {
// add row the list
mHideRowList.put(row, getRowValueFromPosition(row));
// remove row model from adapter
mTableView.getAdapter().removeRow(viewRow);
} else {
Log.e(LOG_TAG, "This row is already hidden.");
}
}
public void showRow(int row) {
showRow(row, true);
}
private void showRow(int row, boolean removeFromList) {
Row hiddenRow = mHideRowList.get(row);
if (hiddenRow != null) {
// add row model to the adapter
mTableView.getAdapter().addRow(row, hiddenRow.getRowHeaderModel(),
hiddenRow.getCellModelList());
} else {
Log.e(LOG_TAG, "This row is already visible.");
}
if (removeFromList) {
mHideRowList.remove(row);
}
}
public void clearHideRowList() {
mHideRowList.clear();
}
public void showAllHiddenRows() {
for (int i = 0; i < mHideRowList.size(); i++) {
int row = mHideRowList.keyAt(i);
showRow(row, false);
}
clearHideRowList();
}
public boolean isRowVisible(int row) {
return mHideRowList.get(row) == null;
}
public void hideColumn(int column) {
int viewColumn = convertIndexToViewIndex(column, mHideColumnList);
if (mHideColumnList.get(column) == null) {
// add column the list
mHideColumnList.put(column, getColumnValueFromPosition(column));
// remove row model from adapter
mTableView.getAdapter().removeColumn(viewColumn);
} else {
Log.e(LOG_TAG, "This column is already hidden.");
}
}
public void showColumn(int column) {
showColumn(column, true);
}
private void showColumn(int column, boolean removeFromList) {
Column hiddenColumn = mHideColumnList.get(column);
if (hiddenColumn != null) {
// add column model to the adapter
mTableView.getAdapter().addColumn(column, hiddenColumn.getColumnHeaderModel(),
hiddenColumn.getCellModelList());
} else {
Log.e(LOG_TAG, "This column is already visible.");
}
if (removeFromList) {
mHideColumnList.remove(column);
}
}
public void clearHideColumnList() {
mHideColumnList.clear();
}
public void showAllHiddenColumns() {
for (int i = 0; i < mHideColumnList.size(); i++) {
int column = mHideColumnList.keyAt(i);
showColumn(column, false);
}
clearHideColumnList();
}
public boolean isColumnVisible(int column) {
return mHideColumnList.get(column) == null;
}
/**
* Hiding row and column process needs to consider the hidden rows or columns with a smaller
* index to be able hide the correct index.
*
* @param index, stands for column or row index.
* @param list, stands for HideRowList or HideColumnList
*/
private int getSmallerHiddenCount(int index, SparseArray list) {
int count = 0;
for (int i = 0; i < index && i < list.size(); i++) {
if (list.valueAt(i) != null) {
count++;
}
}
return count;
}
/**
* It converts model index to View index considering the previous hidden rows or columns. So,
* when we add or remove any item of RecyclerView, we need to view index.
*/
private int convertIndexToViewIndex(int index, SparseArray list) {
return index - getSmallerHiddenCount(index, list);
}
static class Row {
private final int mYPosition;
@Nullable
private final Object mRowHeaderModel;
@Nullable
private final List