Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion WheelCurvedPicker.android.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class WheelCurvedPicker extends React.Component {
itemSpace: 20
}

componentWillReceiveProps (props) {
UNSAFE_componentWillReceiveProps (props) {
this.setState(this._stateFromProps(props));
}

Expand Down
154 changes: 151 additions & 3 deletions android/src/main/java/com/zyu/ReactWheelCurvedPicker.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Shader;
import android.os.SystemClock;
import android.util.AttributeSet;

import com.aigestudio.wheelpicker.core.AbstractWheelPicker;

import com.aigestudio.wheelpicker.view.WheelCurvedPicker;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactContext;
Expand All @@ -17,7 +15,15 @@
import com.facebook.react.uimanager.events.Event;
import com.facebook.react.uimanager.events.EventDispatcher;
import com.facebook.react.uimanager.events.RCTEventEmitter;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeProvider;
import android.graphics.Rect;
import android.view.View;
import android.text.TextUtils;
import android.os.Bundle;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
Expand All @@ -33,6 +39,9 @@ public class ReactWheelCurvedPicker extends WheelCurvedPicker {
private Integer mLinegradientFrom = Color.BLACK; // Default starting gradient color
private Integer mLinegradientTo = Color.WHITE; // Default end gradient color

/** The instance of the node provider for the virtual tree - lazily instantiated. */
private AccessibilityNodeProvider mAccessibilityNodeProvider;

public ReactWheelCurvedPicker(ReactContext reactContext) {
super(reactContext);
mEventDispatcher = reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher();
Expand All @@ -55,6 +64,29 @@ public void onWheelScrollStateChanged(int state) {
});
}

@Override
public void onInitializeAccessibilityNodeInfo (AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);

Rect r = new Rect();
boolean isVisible = this.getGlobalVisibleRect(r);
info.setBoundsInScreen(r);
info.setVisibleToUser(isVisible);
info.setClassName(this.getClass().getCanonicalName());
info.setPackageName(getContext().getPackageName());
info.setScrollable(isVisible);
info.setEnabled(true);
info.setText(data.get(itemIndex));
}

@Override
public AccessibilityNodeProvider getAccessibilityNodeProvider() {
if (mAccessibilityNodeProvider == null) {
mAccessibilityNodeProvider = new VirtualDescendantsProvider();
}
return mAccessibilityNodeProvider;
}

@Override
protected void drawForeground(Canvas canvas) {
super.drawForeground(canvas);
Expand Down Expand Up @@ -102,6 +134,122 @@ public void setValueData(List<Integer> data) {
public int getState() {
return state;
}

private class VirtualDescendantsProvider extends AccessibilityNodeProvider {
@Override
public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) {
AccessibilityNodeInfo info = null;
ReactWheelCurvedPicker root = ReactWheelCurvedPicker.this;

if (virtualViewId == View.NO_ID) {
info = AccessibilityNodeInfo.obtain(root);
onInitializeAccessibilityNodeInfo(info);

int childCount = root.mValueData != null ? root.mValueData.size() : 0;
int sideSize = (root.itemCount % 2 == 0 ? root.itemCount : (root.itemCount - 1))/2;
int minIndex = Math.max(root.itemIndex - sideSize,0);
int maxIndex = Math.min(root.itemIndex + sideSize, childCount-1);

for (int i = minIndex; i <= maxIndex; ++i) {
info.addChild(root, i);
}
} else {
info = AccessibilityNodeInfo.obtain();
info.setClassName(root.getClass().getName() + "Item");
info.setPackageName(getContext().getPackageName());
info.setSource(root, virtualViewId);

// A Naive computation of bounds per item, by dividing global space
// to slots per itemsCount, and figuring out the right position
// as offset from the selected item, which is the center
int childCount = root.mValueData != null ? root.mValueData.size() : 0;
int sideSize = (root.itemCount % 2 == 0 ? root.itemCount : (root.itemCount - 1))/2;
int minIndex = Math.max(root.itemIndex - sideSize,0);
int maxIndex = Math.min(root.itemIndex + sideSize, childCount-1);
boolean isInView = (virtualViewId>= minIndex && virtualViewId<=maxIndex);

Rect r = new Rect();
boolean isVisible = isInView && root.getGlobalVisibleRect(r);

if(isInView) {
int itemHeight = r.height()/root.itemCount;
r.top += (virtualViewId - root.itemIndex + sideSize)*itemHeight;
r.bottom = r.top + itemHeight;
}
else {
r.top = r.bottom = r.left = r.right = 0;
}

info.setBoundsInScreen(r);
info.setVisibleToUser(isVisible);
info.setParent(root);
info.setText(root.data.get(virtualViewId));
info.setSelected(root.itemIndex == virtualViewId);
info.setEnabled(true);
}
return info;
}

@Override
public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String searched,
int virtualViewId) {
if (TextUtils.isEmpty(searched)) {
return Collections.emptyList();
}
String searchedLowerCase = searched.toLowerCase();
List<AccessibilityNodeInfo> result = null;
ReactWheelCurvedPicker root = ReactWheelCurvedPicker.this;

if (virtualViewId == View.NO_ID) {
for (int i = 0; i < root.data.size(); i++) {
String textToLowerCase = root.data.get(i).toLowerCase();
if (textToLowerCase.contains(searchedLowerCase)) {
if (result == null) {
result = new ArrayList<AccessibilityNodeInfo>();
}
result.add(createAccessibilityNodeInfo(i));
}
}
} else {
String textToLowerCase = root.data.get(virtualViewId).toLowerCase();
if (textToLowerCase.contains(searchedLowerCase)) {
result = new ArrayList<AccessibilityNodeInfo>();
result.add(createAccessibilityNodeInfo(virtualViewId));
}
}
if (result == null) {
return Collections.emptyList();
}
return result;
}

@Override
public boolean performAction(int virtualViewId, int action, Bundle arguments) {

ReactWheelCurvedPicker root = ReactWheelCurvedPicker.this;

if (virtualViewId == View.NO_ID) {
switch (action) {
// Allowing lowercase search as in the implementation of findAccessibilityNodeInfosByText
case AccessibilityNodeInfo.ACTION_SET_TEXT:
CharSequence chars = arguments.getCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE);
String searched = chars.toString();
if(TextUtils.isEmpty(searched))
return true; // ignore empty text
String searchedLowerCase = searched.toLowerCase();
for (int i = 0; i < root.data.size(); i++) {
String textToLowerCase = root.data.get(i).toLowerCase();
if (textToLowerCase.contains(searchedLowerCase)) {
root.setItemIndex(i);
break;
}
}
return true;
}
}
return false;
}
}
}

class ItemSelectedEvent extends Event<ItemSelectedEvent> {
Expand Down
2 changes: 1 addition & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
import React from 'react';

import {
PickerIOS,
Platform,
} from 'react-native';
import { PickerIOS } from '@react-native-community/picker'

import WheelCurvedPicker from './WheelCurvedPicker'

Expand Down
11 changes: 8 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-native-wheel-picker",
"version": "1.2.0",
"version": "1.3.0",
"description": "React native cross platform picker.",
"main": "index.js",
"scripts": {
Expand All @@ -13,13 +13,18 @@
"keywords": [
"react-native",
"picker",
"wheel"
"wheel"
],
"peerDependencies": {
"react-native": ">=0.62"
},
"author": "Yu Zheng",
"license": "ISC",
"bugs": {
"url": "https://github.com/lesliesam/react-native-wheel-picker/issues"
},
"homepage": "https://github.com/lesliesam/react-native-wheel-picker#readme",
"dependencies": {}
"dependencies": {
"@react-native-community/picker": "^1.8.1"
}
}