diff --git a/WheelCurvedPicker.android.js b/WheelCurvedPicker.android.js index d123c90..6d83d13 100644 --- a/WheelCurvedPicker.android.js +++ b/WheelCurvedPicker.android.js @@ -41,7 +41,7 @@ class WheelCurvedPicker extends React.Component { itemSpace: 20 } - componentWillReceiveProps (props) { + UNSAFE_componentWillReceiveProps (props) { this.setState(this._stateFromProps(props)); } diff --git a/android/src/main/java/com/zyu/ReactWheelCurvedPicker.java b/android/src/main/java/com/zyu/ReactWheelCurvedPicker.java index 0999ff3..2c1e270 100644 --- a/android/src/main/java/com/zyu/ReactWheelCurvedPicker.java +++ b/android/src/main/java/com/zyu/ReactWheelCurvedPicker.java @@ -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; @@ -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; /** @@ -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(); @@ -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); @@ -102,6 +134,122 @@ public void setValueData(List 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 findAccessibilityNodeInfosByText(String searched, + int virtualViewId) { + if (TextUtils.isEmpty(searched)) { + return Collections.emptyList(); + } + String searchedLowerCase = searched.toLowerCase(); + List 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(); + } + result.add(createAccessibilityNodeInfo(i)); + } + } + } else { + String textToLowerCase = root.data.get(virtualViewId).toLowerCase(); + if (textToLowerCase.contains(searchedLowerCase)) { + result = new ArrayList(); + 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 { diff --git a/index.js b/index.js index bc7ed07..7017bcd 100644 --- a/index.js +++ b/index.js @@ -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' diff --git a/package.json b/package.json index 6f285dc..93815b6 100644 --- a/package.json +++ b/package.json @@ -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": { @@ -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" + } }