View的分类与流程

一.自定义View分类 我将自定义View分为了两类(sloop个人分类法,非官方): 1.自定义ViewGroup 自定义ViewGroup一般是利用现有的组件根据特定的布局方式来组成新的组件,大多继承自ViewGroup或各种Layout,包含有子View。 例如:应用底部导航条中的条目,一般都是上面图标(ImageView),下面文字(TextView),那么这两个就可以用自定义ViewGroup组合成为一个Veiw,提供两个属性分别用来设置文字和图片,使用起来会更加方便。 2.自定义View 在没有现成的View,需要自己实现的时候,就使用自定义View,一般继承自View,SurfaceView或其他的View,不包含子View。 例如:制作一个支持自动加载网络图片的ImageView,制作图表等。 PS: 自定义View在大多数情况下都有替代方案,利用图片或者组合动画来实现,但是使用后者可能会面临内存耗费过大,制作麻烦等诸多问题。 二.几个重要的函数 1.构造函数 构造函数是View的入口,可以用于初始化一些的内容,和获取自定义属性。 View的构造函数有四种重载分别如下: public void SloopView(Context context) {} public void SloopView(Context context, AttributeSet attrs) {} public void SloopView(Context context, AttributeSet attrs, int defStyleAttr) {} public void SloopView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {} 可以看出,关于View构造函数的参数有多有少,先排除几个不常用的,留下常用的再研究。 有四个参数的构造函数在API21的时候才添加上,暂不考虑。 有三个参数的构造函数中第三个参数是默认的Style,这里的默认的Style是指它在当前Application或Activity所用的Theme中的默认Style,且只有在明确调用的时候才会生效,以系统中的ImageButton为例说明: public ImageButton(Context context, AttributeSet attrs) { //调用了三个参数的构造函数,明确指定第三个参数 this(context, attrs, com.android.internal.R.attr.imageButtonStyle);}public ImageButton(Context context, AttributeSet attrs, int defStyleAttr) { //此处调了四个参数的构造函数,无视即可 this(context, attrs, defStyleAttr, 0); }复制代码注意:即使你在View中使用了Style这个属性也不会调用三个参数的构造函数,所调用的依旧是两个参数的构造函数。 ...

October 17, 2019 · 2 min · jiezi

AndroidViewStub

ViewStub是一个轻量级View,它是一个看不见的,并且不占布局位置,占用资源非常小的视图对象。可以为ViewStub指定一个布局,加载布局时,只有ViewStub会被初始化,然后当ViewStub被设置为可见时,或者是调用了ViewStub.inflate()时,ViewStub所指向的布局会被加载和实例化,然后ViewStub的布局属性都会传给它指向的布局。这样就可以使用ViewStub来设置是否显示某个布局。 <ViewStub android:inflatedId="@+id/inflatedId" android:id="@+id/viewStub" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout="@layout/layout_inflate_view_stub"/>ViewStub是继承自View,设置它的可见性,可以通过ViewStub#setVisibility()和ViewStub#inflate()。先看ViewStub#setVisibility()方法。 private WeakReference<View> mInflatedViewRef; public void setVisibility(int visibility) { if (mInflatedViewRef != null) { View view = mInflatedViewRef.get(); if (view != null) { view.setVisibility(visibility); } else { throw new IllegalStateException("setVisibility called on un-referenced view"); } } else { super.setVisibility(visibility); if (visibility == VISIBLE || visibility == INVISIBLE) { inflate(); } } }初次调用该方法,mInflatedViewRef=null。因此会先走else逻辑。先通过super.setVisibility()调用View的setVisibility方法,更改相关的标志。然后无论设置的是View.VISIBLE或者是View.INVISIBLE都会去调用ViewStub#inflate()方法。如果mInflatedViewRef!=null,就是正常的设置可见性。再看View#inflate()方法。在xml布局中ViewStub节点下android:layout引入的布局会将ViewStub替换掉,并且ViewStub的宽高等属性会被该布局使用。 public View inflate() { final ViewParent viewParent = getParent(); if (viewParent != null && viewParent instanceof ViewGroup) { if (mLayoutResource != 0) { final ViewGroup parent = (ViewGroup) viewParent; /* inflateViewNoAdd()方法被填充的布局生成View对象。如果为ViewStub设置了属性inflatedId,那么这里的view的id会被替换成该inflatedId。 */ final View view = inflateViewNoAdd(parent); //将ViewStub替换成被填充的布局,并使用了ViewStub的属性。 replaceSelfWithView(view, parent); mInflatedViewRef = new WeakReference<>(view); if (mInflateListener != null) { //监听事件需要在inflate()或者setVisibility()之前调用。 mInflateListener.onInflate(this, view); } return view; } else { throw new IllegalArgumentException("ViewStub must have a valid layoutResource"); } } else { throw new IllegalStateException("ViewStub must have a non-null ViewGroup viewParent"); } }最后说一下id相关。 ...

July 8, 2019 · 1 min · jiezi

IDEA-常用插件

帮助 JAVA 开发者提升开发效率的插件工具插件名称功能描述备注lombok注解方式实现 Setter 和 Getter 等方法可以大量减少模板代码的编写工作,代码更整洁backgroundimage plus设置IDEA 的背景图片 CodeGlance相当于当前代码文件的缩略图,可以快速移动到代码块IDEA 代码界面右侧的缩略预览Idea restartIDEA 的重启功能因 IDEA 默认未自带重启功能,使用该插件可实现mybatis log pluginMybatis 的日志插件,可在 IDEA 控制台看到完整可执行的 Sql 语句 translation翻译插件 markdown supportmarkdown 文件支持可以打开 markdown 文件并以 markdown 的语法渲览文档free mybatis pluginMybatis 的插件,可以在interface 和 sql 的 xml 文件间跳转 maven helpermaven 依赖包的可视化展示对于 maven 项目的依赖包的处理很有帮助gson format将 JSON 转为 java 类可以直接根据 JSON 数据定义出 java 类及其 fieldgrep console控制台日志展示不同级别的日志可以以不同的颜色展示Material ThemeIDEA 的主题插件,可更换 IDEA 的展示风格可选主题比较多,个人比较喜欢

May 22, 2019 · 1 min · jiezi

10Laravel使用视图组合器View-composer

Laravel的视图组合器很有用,在网站中, 许多页面的侧边栏是相同的,将侧边栏公用部分提取出来肯定是必须的,让Controller专注于业务逻辑。参考:https://laravel.com/docs/5.5/... 创建一个新的 provider类 ViewComposerServiceProvider php artisan make:provider ViewComposerServiceProvider编辑ViewComposerServiceProvider类文件中boot()方法,添加如下代码: // 参数'*':代表所有视图,其实可以指定视图// \App\Http\ViewComposers\PublicComposer:公共视图的业务逻辑,目录位置是自定义的\View::Composer('*', '\App\Http\ViewComposers\PublicComposer');在ConfigApp.php配置文件中配置$provider数组,加入自定义的ViewComposerServiceProvider类 App\Providers\ViewComposerServiceProvider::class编辑AppHttpViewComposersPublicComposer类文件中compose()方法,添加公共数据的业务逻辑 public function compose(View $view){ $categories = $this->cache('categories', function () { // 获取数据逻辑 return Category::all(); }, 60*24); $tags = $this->cache('tags', function () { return Tag::all(); }, 60*24); // $view->with()方法绑定参数到视图 $view->with(compact('categories', 'tags'));}// 封装一个缓存处理方法private function cache($key, $callback, $time = 60){ $key_result = []; if (Cache::has($key)) { $key_result = Cache::get($key); } else { $key_result = $callback(); Cache::put($key, $key_result, $time); } return $key_result;}原文链接:http://www.mi360.cn/articles/4 ...

May 21, 2019 · 1 min · jiezi

ItemTouchHelper 实现可拖拽和侧滑的 RecyclerView

前言话不多说,直接上图:这里是项目的【GitHub地址】 。笔者使用 RecyclerView 的 ItemTouchHelper 来实现这个效果,过程非常简单。实现功能:按住 item 左侧的按钮可以上下拖动 item向右侧滑删除 itemitem 拖动或侧滑时有阴影效果实现基本功能循序渐进学习,这里我们先实现基本的功能:长按 item 实现上下拖拽向右侧滑删除布局文件很简单,不多说,直接上代码:activity_main.xml<?xml version=“1.0” encoding=“utf-8”?><LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android" android:layout_width=“match_parent” android:layout_height=“match_parent”> <android.support.v7.widget.RecyclerView android:layout_width=“match_parent” android:layout_height=“match_parent” android:id=”@+id/main_recyclerView" android:scrollbars=“none” android:background="#F2F8FC"> </android.support.v7.widget.RecyclerView></LinearLayout>item_list.xml<?xml version=“1.0” encoding=“utf-8”?><android.support.constraint.ConstraintLayout xmlns:android=“http://schemas.android.com/apk/res/android" xmlns:app=“http://schemas.android.com/apk/res-auto" android:layout_width=“match_parent” android:layout_height=“60dp” android:background="#ffffff”> <ImageView android:id=”@+id/item_list_menu_imageView" android:layout_width=“30dp” android:layout_height=“25dp” android:layout_marginBottom=“8dp” android:layout_marginStart=“15dp” android:layout_marginTop=“8dp” android:src="@drawable/imageview_menu" app:layout_constraintBottom_toBottomOf=“parent” app:layout_constraintStart_toStartOf=“parent” app:layout_constraintTop_toTopOf=“parent” /> <TextView android:id="@+id/item_list_text_textView" android:layout_width=“wrap_content” android:layout_height=“wrap_content” android:layout_marginBottom=“8dp” android:layout_marginStart=“15dp” android:layout_marginTop=“8dp” android:textColor="#000000" android:textSize=“18sp” app:layout_constraintBottom_toBottomOf=“parent” app:layout_constraintStart_toEndOf="@+id/item_list_menu_imageView" app:layout_constraintTop_toTopOf=“parent”/> <android.support.v7.widget.SwitchCompat android:id="@+id/item_list_switchCompat" android:layout_width=“wrap_content” android:layout_height=“wrap_content” android:layout_marginBottom=“8dp” android:layout_marginEnd=“15dp” android:layout_marginTop=“8dp” app:layout_constraintBottom_toBottomOf=“parent” app:layout_constraintEnd_toEndOf=“parent” app:layout_constraintTop_toTopOf=“parent” /> </android.support.constraint.ConstraintLayout>ItemTouchHelper官方的API解释:This is a utility class to add swipe to dismiss and drag & drop support to RecyclerView.这是一个给 RecyclerView 添加侧滑删除和拖拽的工具类。有了它,我们就可以很方便的实现上面的效果。从构造器: ItemTouchHelper(ItemTouchHelper.Callback callback) 可以看出,我们需要一个 ItemTouchHelper.Callback 去构造 ItemTouchHelper 。ItemTouchHelper.Callback 官方提供了一个实现类 ItemTouchHelper.SimpleCallback() ,它的使用很简单,但为了得到更高的定制性,这里我们不使用它,有兴趣的朋友可以去尝试一下。新建一个 MyItemTouchHelperCallback 类继承 ItemTouchHelper.Callback ,这里我们关注它其中的三个方法:getMovementFlags() 定义 item 的可以拖拽和滑动的方向。onMove() 当 item 想要上下拖拽时会调用此方法。onSwiped 当 item 想要左右侧滑时会调用此方法。在写 MyItemTouchHelperCallback 之前我们先创建一个 IItemTouchHelperAdapter 接口,让 RecyclerViewAdapter 实现这个接口。用于 RecyclerViewAdapter 回调。public interface IItemTouchHelperAdapter { /** * 当item被移动时调用 * * @param fromPosition 被操作的item的起点 * @param toPosition 被操作的item的终点 / void onItemMove(int fromPosition, int toPosition); /* * 当item被侧滑时调用 * * @param position 被侧滑的item的position / void onItemDismiss(int position);}在 RecyclerViewAdapter 中实现刚刚继承的两个方法: @Override public void onItemMove(int fromPosition, int toPosition) { Collections.swap(mList, fromPosition, toPosition); notifyItemMoved(fromPosition, toPosition); } @Override public void onItemDismiss(int position) { mList.remove(position); notifyItemRemoved(position); }之后我们就可以开始编写 MyItemTouchHelperCallback ,注释很清楚,就不多说了。public class MyItemTouchHelperCallback extends ItemTouchHelper.Callback { private IItemTouchHelperAdapter mAdapter; public MyItemTouchHelperCallback(IItemTouchHelperAdapter mAdapter) { this.mAdapter = mAdapter; } @Override public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { //上下拖拽,若有其他需求同理 int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN; //向右侧滑,若有其他需求同理 int swipeFlags = ItemTouchHelper.RIGHT; return makeMovementFlags(dragFlags, swipeFlags); } @Override public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { //通知Adapter更新数据和视图 mAdapter.onItemMove(viewHolder.getAdapterPosition(), target.getAdapterPosition()); //若返回false则表示不支持上下拖拽 return true; } @Override public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { //通知Adapter更新数据和视图 mAdapter.onItemDismiss(viewHolder.getAdapterPosition()); } @Override public boolean isItemViewSwipeEnabled() { //是否可以左右侧滑,默认返回true return true; } @Override public boolean isLongPressDragEnabled() { //是否可以长按上下拖拽,默认返回false return true; }}最后,在 Acivity 中将 ItemTouchHelper 和 RecyclerView 关联起来 mItemTouchHelper = new ItemTouchHelper(new MyItemTouchHelperCallback(adapter)); mItemTouchHelper.attachToRecyclerView(recyclerView);运行效果:完善我们实现的的基本效果与文章开头给出的效果还是有一点差距的,还需要实现的效果:通过按住 item 左边按钮才能上下拖拽。侧滑或拖拽时被操作的 item Z轴高度增加,有明显的阴影。通过按住 item 左边按钮才能上下拖拽首先修改 MyItemTouchHelperCallback 的 isLongPressDragEnabled() @Override public boolean isLongPressDragEnabled() { //禁止长按item可以上下拖拽,因为我们要自定义开启拖拽的时机 return false; }其次新建一个OnStartDragListener接口,用于回调public interface OnStartDragListener { /* * 当View需要拖拽时回调 * * @param viewHolder The holder of view to drag / void onStartDrag(RecyclerView.ViewHolder viewHolder);}让 Activity 继承这个类,实现 onStartDrag() 方法@Override public void onStartDrag(RecyclerView.ViewHolder viewHolder) { //通知ItemTouchHelper开始拖拽 mItemTouchHelper.startDrag(viewHolder); }在 RecyclerViewAdapter 的构造方法中传入 OnStartDragListener 的实例(即实现了该接口的Activity),给 item 左侧的按钮添加事件监听public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ItemViewHolder> implements IItemTouchHelperAdapter { //构造方法 public RecyclerViewAdapter(List<ItemEntity> list, OnStartDragListener mDragListener) { mList = list; this.mDragListener = mDragListener; } … @Override public void onBindViewHolder(final IItemViewHolder holder, @SuppressLint(“RecyclerView”) final int position) { … holder.menu.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View view, MotionEvent motionEvent) { if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) { //通知ItemTouchHelper开始拖拽 mDragListener.onStartDrag(holder); } return false; } }); } …}侧滑或拖拽时被操作的 item Z轴高度增加,有明显的阴影要实现这个效果,笔者使用 ItemTouchHelper.Callback 提供的两个回调方法:onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) :当 ViewHolder (即 item )滑动或拖动时被调用。clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) :当 ViewHolder 滑动和拖拽结束时被调用首先新建一个 IItemTouchHelperViewHolder 接口用于回调,让 RecyclerViewAdapter 的 ViewHolder 继承它。public interface IItemTouchHelperViewHolder { /* * item被选中,在侧滑或拖拽过程中更新状态 / void onItemSelected(); /* * item的拖拽或侧滑结束,恢复默认的状态 */ void onItemClear();}然后让ViewHolder重写上述的两个方法: class ItemViewHolder extends RecyclerView.ViewHolder implements IItemTouchHelperViewHolder { private TextView text; private ImageView menu; private SwitchCompat switchCompat; ItemViewHolder(View itemView) { super(itemView); text = itemView.findViewById(R.id.item_list_text_textView); menu = itemView.findViewById(R.id.item_list_menu_imageView); switchCompat = itemView.findViewById(R.id.item_list_switchCompat); } @Override public void onItemSelected() { itemView.setTranslationZ(10); } @Override public void onItemClear() { itemView.setTranslationZ(0); }}这里通过 setTranslationZ() 来改变 itemView 的高度。之前笔者尝试通过 setElevation() 来改变高度,但是行不通,后来查资料发现 View 的Z轴阴影Z = elevation + translationZ 其中:elevation 是静态值,是 View 在Z轴上的初始值translationZ 是动态值,是Z上的偏移变化所以我们这里应该使用 setTranslationZ() 来改变 View 的Z轴高度而不是使用 setElevation() ,读者们可以自行尝试一下。最后修改 MyItemTouchHelperCallback ,重写上述的两个方法,过程很简单,直接看代码: @Override public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) { if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) { //不为空闲状态,即为拖拽或侧滑状态 if (viewHolder instanceof IItemTouchHelperViewHolder) { IItemTouchHelperViewHolder itemTouchHelperViewHolder = (IItemTouchHelperViewHolder) viewHolder; itemTouchHelperViewHolder.onItemSelected(); } } super.onSelectedChanged(viewHolder, actionState); } @Override public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { super.clearView(recyclerView, viewHolder); if (viewHolder instanceof IItemTouchHelperViewHolder) { IItemTouchHelperViewHolder itemTouchHelperViewHolder = (IItemTouchHelperViewHolder) viewHolder; itemTouchHelperViewHolder.onItemClear(); } }最后如果 SwipeAndDragRecyclerView 对您有任何帮助,希望能得到您在 GitHub 的一个 Star !如果文章中有什么错误或值得改进的地方,欢迎到评论区中给我留言 ...

February 1, 2019 · 3 min · jiezi

仿知乎分享界面

前言最近在做一个资讯类的APP,上面需要一个分享功能,项目不大,如果去使用官方的SDK还需要审查之类的,感觉太麻烦。偶然看到知乎的分享界面做得不错,拿到我这个项目中正合适,在网上查了一下资料,使用BottomSheetDialogFragment结合系统自带的分享功能就可以做到它的效果。知乎分享界面:自己完成的效果图:)布局文件<LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android" android:layout_width=“match_parent” android:layout_height=“wrap_content” android:orientation=“vertical”> <TextView android:id=”@+id/fragment_share_textView" android:layout_width=“wrap_content” android:layout_height=“wrap_content” android:layout_marginStart=“24dp” android:layout_marginTop=“16dp” android:text="@string/shareTo" android:textColor="@color/black" android:gravity=“center_vertical” android:textSize=“16sp”/> <android.support.v7.widget.RecyclerView android:id="@+id/fragment_share_recyclerView" android:layout_width=“match_parent” android:layout_height=“wrap_content” android:layout_marginTop=“8dp” android:layout_marginBottom=“8dp” /></LinearLayout>数据获取手机中可接收我们分享的数据的App集合,方法很简单: public static List<ResolveInfo> getShareList(Context context) { Intent intent = new Intent(Intent.ACTION_SEND); intent.setType(“text/plain”); PackageManager packageManager = context.getPackageManager(); List<ResolveInfo> list = packageManager.queryIntentActivities(intent, 0); return list; }如果你只想显示指定的APP,或者把一些APP的显示位置提前,在这里可以通过APP的包名进行筛选或过滤。例如你想把腾讯旗下的APP提前一点,可以这么做: //调整顺序,把微信、QQ提到前面来 Collections.sort(list, new Comparator<ResolveInfo>() { @Override public int compare(ResolveInfo resolveInfo, ResolveInfo t1) { ActivityInfo activityInfo1 = resolveInfo.activityInfo; ActivityInfo activityInfo2 = t1.activityInfo; if (activityInfo1.packageName.contains(“com.tencent.”) && !activityInfo2.packageName.contains(“com.tencent.”)) { return -1; } else if (!activityInfo1.packageName.contains(“com.tencent.”) && activityInfo2.packageName.contains(“com.tencent.”)) { return 1; } return 0; } });之后我们就能很容易获取到这些App的名字和图标。Drawable icon = list.get(i).loadIcon(context.getPackageManager());String label = list.get(i).loadLabel(context.getPackageManager()).toString();界面代码新建一个Fragment继承BottomSheetDialogFragment,BottomSheetDialogFragment的使用很简单,界面代码的编写与在普通的Fragment一样,然后我们只需调用它的show()/dismiss()即可让它显示或关闭。完整的shareFragment代码:public class ShareFragment extends BottomSheetDialogFragment { private List<ResolveInfo> mShareResolveInfoList; private List<ShareItem> mShareList; private Context mContext; private static String mTitle; private static String mUrl; public static ShareFragment getInstance(String title, String url) { mTitle = title; mUrl = url; return new ShareFragment(); } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { mContext = getContext(); View view = inflater.inflate(R.layout.fragment_share, container, false); initData(); initViews(view); return view; } private void initViews(View view) { RecyclerView recyclerView = view.findViewById(R.id.fragment_share_recyclerView); recyclerView.setLayoutManager(new GridLayoutManager(mContext, 3)); ShareRecyclerViewAdapter adapter = new ShareRecyclerViewAdapter(mShareList, mContext); adapter.setOnClickShareItemListener(new ShareRecyclerViewAdapter.OnClickShareItemListener() { @Override public void OnClick(int position) { Intent intent = new Intent(Intent.ACTION_SEND); intent.putExtra(Intent.EXTRA_TEXT, “我在讯博上发现一篇好文章:” + “\n” + mTitle + “\n” + mUrl); intent.setType(“text/plain”); ActivityInfo activityInfo = mShareResolveInfoList.get(position).activityInfo; intent.setClassName(activityInfo.packageName, activityInfo.name); startActivity(intent); dismiss(); } }); recyclerView.setAdapter(adapter); } private void initData() { mShareList = new ArrayList<>(); mShareResolveInfoList = ShareUtil.getShareList(mContext); for (int i = 0; i < mShareResolveInfoList.size(); i++) { ShareItem item = new ShareItem(); item.setIcon(mShareResolveInfoList.get(i).loadIcon(mContext.getPackageManager())); item.setLabel(mShareResolveInfoList.get(i).loadLabel(mContext.getPackageManager()).toString()); mShareList.add(item); } }}这里我实现的是分享文字,如果要分享其他东西例如图片,把intent.setType(“text/plain”)修改成intent.setType(“image/jpeg”),再在intent.putExtra()中传去入分享的东西即可。把分享的显示封装成一个工具类方法,方便在任何界面调用 public static void share(FragmentManager fragmentManager, String title, int id) { String url = “xxx”; ShareFragment.getInstance(title, url).show(fragmentManager, “dialog”); }最后文章中如果有什么错误或可以改进的地方,欢迎在评论区给我留言。 ...

February 1, 2019 · 2 min · jiezi

SAP S/4HANA CDS View的访问控制实现:DCL介绍

来自我的同事Xu MilesAuthorization Objects are business concept, they are distinguished by business scenario.Therefore, there might be a lot of Authorization Objects using the same Authorization Field. Such as VKORG.In Sales Planning, there’re two Authorization Objects related to Sales Area, V_VBAK_VKO and V_VBRK_VKO, they represent transaction scenario Sales Document and Billing Document repectively. If we want to extract the Sales Area master data, technically we could use any Authorization Object with Authorization Field VKORG, VTWEG, SPART.However, considering the authorization of the business user, we need to use Authorization Object V_VBAK_VKO and V_VBRK_VKO separately when end user is planning for income sales or sales volume.To do this, we could use additional CDS view + DCL to realize.Example on ERG/001:CDS: ZMX_SalesArea_DCLDCL: ZMX_SalesArea_DCLThe logic in DCL:@EndUserText.label: ‘Auto assigned mapping role for ZMX_SalesArea_DCL’@MappingRole: truedefine role ZMX_SalesArea_DCL {grant select on ZMX_SalesArea_DCLwhere (SalesPlanPurpose = ‘0’ and (SalesOrganization, DistributionChannel, Division) =aspect pfcg_auth (V_VBAK_VKO,VKORG,VTWEG,SPART,actvt = ‘03’))OR (SalesPlanPurpose = ‘1’ and (SalesOrganization) =aspect pfcg_auth (V_VBRK_VKO,VKORG,actvt = ‘03’));}The logic in the CDS view:define view ZMX_SalesArea_DCLas select fromI_SalesArea{key SalesOrganization,key DistributionChannel,key Division,‘0’ as SalesPlanPurpose}union all select from I_SalesArea{key SalesOrganization,key DistributionChannel,key Division,‘1’ as SalesPlanPurpose};Authorization test on QW9/910User: MILES01Authorization:Test Report:REPORT mx_test_sa_dcl.PARAMETERS:p_purp TYPE C LENGTH 1.DATA:lt_tab TYPE STANDARD TABLE OF i_salesarea_dcl.SELECT * FROM i_salesarea_dcl INTO TABLE @lt_tabWHERE salesplanpurpose = @p_purp.LOOP AT lt_tab ASSIGNING FIELD-SYMBOL(<fs_row>).WRITE:/ <fs_row>-salesorganization, <fs_row>-distributionchannel, <fs_row>-division, <fs_row>-salesplanpurpose.ENDLOOP.WRITE:/ ‘Finished.’.The logic flow is that firstly we use SalesPlanUUID to get SalesPlanPurpose, then with SalesPlanPurpose we could get the Sales Area master data via the corresponding Authorization Objects.SalesPlanPurpose: 0 (Incoming sales)SalesPlanPurpose: 1(Sales Volume)要获取更多Jerry的原创文章,请关注公众号"汪子熙": ...

January 22, 2019 · 2 min · jiezi

在Flutter中嵌入Native组件的正确姿势是...

引言在漫长的从Native向Flutter过渡的混合工程时期,要想平滑地过渡,在Flutter中使用Native中较为完善的控件会是一个很好的选择。本文希望向大家介绍AndroidView的使用方式以及在此基础之上拓展的双端嵌入Native组件的解决方案。1. 使用教程1.1. DemoRun嵌入地图这一场景可能在很多App中都会存在,但是现在的地图SDK都没有提供Flutter的库,而自己开发一套地图显然不太现实。这种场景下,使用混合栈的形式是一个比较好的选择。我们可以直接在Native的绘图树中嵌入一个Map,但是这个方案嵌入的View并不在Flutter的绘图树中,是一种比较暴力且不优雅的方式,使用起来也很费劲。这时候,使用Flutter官方提供的控件AndroidView就是一种比较优雅的解决方案了。这里做了一个简单的嵌入高德地图的demo,就让我们跟着这个应用场景,看一下AndroidView的使用方式和实现原理。1.2. AndroidView使用方式AndroidView的使用方式和MethodChannel类似,比较简单,主要分为三个步骤:第一步:在dart代码的相应位置使用AndroidView,使用时需要传入一个viewType,这个String将用于唯一标识该Widget,用于和Native的View建立关联。第二步:在native侧添加代码,写一个PlatformViewFactory,PlatformViewFactory的主要任务是,在create()方法中创建一个View并把它传给Flutter(这个说法并不准确,但是我们姑且可以这么理解,后续会进行解释)第三步:使用registerViewFactory()方法注册刚刚写好的PlatformViewFactory,该方法需要传入两个参数,第一个参数需要和之前在Flutter端写的viewType对应,第二个参数是刚刚写好的的PlatformViewFactory。配置高德地图的部分这里就省略不说了,官方有比较详细的文档,可以去高德开发者平台进行查阅。以上便是使用AndroidView的所有操作,总体看起来还是比较简单的,但是真正要用起来,还是有两个无法忽视的问题:View最终的显示尺寸由谁决定?触摸事件是如何处理的?下面就让小闲鱼来给各位一一解答。2. 原理讲解想要解决上面的两个问题,首先必须得理解所谓"传View"的本质是什么?2.1. 所谓"传View"的本质是什么?要解决这个问题,自然避免不了的需要去阅读源码,从更深的层面去看这个传递的整个过程,可以整理出一张这样的流程图:我们可以看到,Flutter最终拿到的是native层返回的一个textureId。根据native的知识ky h这个textureId是已经在native侧渲染好了的view的绘图数据对应的ID,通过这个ID可以直接在GPU中找到相应的绘图数据并使用,那么Flutter是如何去利用这个ID的呢?在之前的深入了解Flutter界面开发中,也给大家介绍了Flutter的绘图流程。我这里也给大家再简单整理一下Flutter的Framework层最后会递交给Engine层一个layerTree,在管线中会遍历layertree的每一个叶子节点,每一个叶子节点最终会调用Skia引擎完成界面元素的绘制,在遍历完成后,在调用glPresentRenderBuffer(IOS)或者glSwapBuffer(Android)按完成上屏操作。Layer的种类有很多,而AndroidView则使用的是其中的TextureLayer。TextureLayer在之前的《Flutter外接纹理》中有更为详细的介绍,这里就不再赘述。TextureLayer在被遍历到时,会调用一个engine层的方法SceneBuilder::addTexture() 将textureId作为参数传入。最终在绘制的时候,skia会直接在GPU中根据textureId找到相应的绘制数据,并将其绘制到屏幕上。那么是不是谁拿到这个ID都可以进行这样的操作呢?答案当然是否定的,Texture数据存储在创建它的EGLContext对应的线程中,所以如果在别的线程进行操作是无法获取到对应的数据的。这里需要引入几个概念:显示屏对象(Display):提供合理的显示器的像素密度和大小的信息Presentation:它给Android提供了在对应的上下文(Context)和显示屏对象(Display)上绘制的能力,通常用于双屏异显。这里不展开讲解Presentation,我们只需要明白Flutter是通过Presentation实现了外接纹理,在创建Presentation时,传入FlutterView对应的Context和创建出来的一个虚拟显示屏对象,使得Flutter可以直接通过ID找到并使用Native创建出来的纹理数据。2.2. View最终的显示尺寸由谁决定?通过上面的流程大家应该都能想到,显示尺寸看起来像是由两部分决定的:AndroidView的大小,Android端View的大小。那么实际上到底是有谁来决定的呢,让我们来做一个实验?直接新建一个Flutter工程,并把中间改成一个AndroidView。//Flutterclass _MyHomePageState extends State<MyHomePage> { double size = 200.0; void _changeSize() { setState(() { size = 100.0; }); } @override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar( title: new Text(widget.title), ), body: Container( color: Color(0xff0000ff), child: SizedBox( width: size, height: size, child: AndroidView( viewType: ’testView’, ), ), ), floatingActionButton: new FloatingActionButton( onPressed: _changeSize, child: new Icon(Icons.add), ), ); }}在Android端也要加上对应的代码,为了更好地看出裁切效果,这里使用ImageView。//Android@Overridepublic PlatformView create(final Context context, int i, Object o) { final ImageView imageView = new ImageView(context); imageView.setLayoutParams(new ViewGroup.LayoutParams(500,500)); imageView.setBackground(context.getResources().getDrawable(R.drawable.idle_fish)); return new PlatformView() { @Override public View getView() { return imageView; } @Override public void dispose() { } };}首先先看AndroidView,AndroidView对应的RenderObject是RenderAndroidView,而一个RenderObject的最终大小的确定是存在两种可能,一种是由父节点所指定,还有一种是在父节点指定的范围中根据自身情况确定大小。打开对应的源码,可以看到其中有个很重要的属性sizedByParent = true,也就是说AndroidView的大小是由其父节点所决定的,我们可以使用Container、SizedBox等控件控制AndroidView的大小。AndroidView的绘图数据是Native层所提供的,那么当Native中渲染的View的实际像素大小大于AndroidView的大小时,会发生什么呢?通常情况下,这种情况的处理思路无非就两种选择,一种是裁切,另一种是缩放。Flutter保持了其一贯的做法,所有out of the bounds的Widget统一使用裁切的方式进行展示,上面所描述的情况就被当作是一种out of the bounds。当这个View的实际像素大小小于AndroidView的时候,会发现View并不会相应地变小(Container的背景色并没有显露出来),没有内容的地方会被白色填充。这其中的原因是SingleViewPresentation::onCreate中,会使用一个FrameLayout作为rootView。2.3. 触摸事件如何传递Android的事件流大家应该都很熟悉了,自顶向下传递,自底向上处理或回流。Flutter同样是使用这一规则,但是其中AndroidView通过两个类来去处理手势:MotionEventsDispatcher:负责将事件封装成Native的事件并向Native传递;AndroidViewGestureRecognizer:负责识别出相应的手势,其中有两个属性:cachedEvents和forwardedPointers,只有当PointerEvent的pointer属性在forwardedPointers中时才会去进行分发,否则会存在cacheEvents中。这里的实现主要是为了解决一些事件的冲突,比如滑动事件,可以通过gestureRecognizers来进行处理,这里可以参考官方注释。/// For example, with the following setup vertical drags will not be dispatched to the Android view as the vertical drag gesture is claimed by the parent [GestureDetector]./// /// GestureDetector(/// onVerticalDragStart: (DragStartDetails d) {},/// child: AndroidView(/// viewType: ‘webview’,/// gestureRecognizers: <OneSequenceGestureRecognizer>[],/// ),/// )/// /// To get the [AndroidView] to claim the vertical drag gestures we can pass a vertical drag gesture recognizer in [gestureRecognizers] e.g:/// /// GestureDetector(/// onVerticalDragStart: (DragStartDetails d) {},/// child: SizedBox(/// width: 200.0,/// height: 100.0,/// child: AndroidView(/// viewType: ‘webview’,/// gestureRecognizers: <OneSequenceGestureRecognizer>[ new VerticalDragGestureRecognizer() ],/// ),/// ),/// )所以总结起来,这部分流程总结起来其实也很简单:事件最初从Native到Flutter这一阶段不在本文的讨论范围之内,Flutter按照自己的规则去处理事件,如果AndroidView赢得了事件,事件就会被封装成相应的Native端的事件并且通过方法通道传回Native,Native再根据自己的处理事件的规则去处理。3. 总结3.1. 方案局限性往大里说:这套方案是Google为了解决开发者日益增长的业务需求与落后的生态环境之间的矛盾而产生的,这一矛盾是一个新生态必然需要去面对的主要矛盾。为了解决这一个问题,最简单的方式当然就是允许开发者使用老生态中已经非常成熟的控件。当然,这样是可以临时解决Flutter生态发展不全面的问题,但是使用这套方案不可避免的需要去编写双端代码(甚至现在iOS还没有对应的控件,当然之后肯定会更新),不能做到真正的跨端。往小里说:这套方案存在着性能上的缺陷,在AndroidView这个类的第三句注释中,官方就已经提到了这是一套比较昂贵的方案,避免在使用Flutter控件也能实现的情况下去使用它。如果之前有看过《Flutter外接纹理》这一文章的同学应该知道,Flutter实现外接纹理的方案中,数据从GPU->CPU->GPU的过程代价是比较大的,在大量使用的场景会造成明显的性能缺陷。我们通过一些手段绕过了中间CPU这一步,并且将这项技术在APP中落地,用于处理图片资源。3.2. 实际应用目前闲鱼从Native向Flutter的迁移工作遇到了Native的本地图片资源在Flutter侧无法访问的问题,在现在Flutter和Native必将长期共存的情况下,重新拷贝一份资源以Flutter的规则来存储当然可以,但是不可避免地增大了包体积,而且不好管理。面对这个问题,我们的解法便是借鉴了AndroidView使用Texture的思路并在将其优化。实现了Native和Flutter的图片资源归一化。除了用于加载位于Native资源目录下的本地图片之外,还可以利用Native的图片库来加载网络图片。我们这么去做的原因是我们在Native侧的图片库较为完善并且经受过大量的线上考验,现在这一阶段,我们不希望将过多的精力投入到重复造轮子这一件事上,而处理网络图片资源和处理本地图片资源的思路其实是一样的,所以我们选择将图片资源进行了统一地整合,在与官方的团队进行沟通并完善后会和大家同步,敬请关注我们的公众号。3.4. 引用高德地图SDK文档万万没想到——Flutter外接纹理Android7.1 Presentation双屏异显原理分析本文作者:闲鱼技术-尘萧阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

November 16, 2018 · 1 min · jiezi