这一篇主要来讲一下自定义控件中的自定义viewgroup,我们以项目中最常用的下拉刷新和加载更多组件为例

简单介绍一下自定义viewgroup时应该怎么做。

分析:下拉刷新和加载更多的原理和步骤

自定义一个viewgroup,将headerview、contentview和footerview从上到下依次布局,然后在初始化的时候

通过Scrooller滚动使得该组件在y轴方向上滚动headerview的高度,这样headerview就被隐藏了。而contentview的

宽度和高度都是match_parent的,因此屏幕上 headerview和footerview就都被隐藏在屏幕之外了。当contentview被

滚动到顶部,如果此时用户继续下拉,那么下拉刷新组件将拦截触摸事件,然后根据用户的触摸事件获取到手指滑动的

y轴距离,并通过scroller将该下拉组件在y轴上滚动手指滑动的距离,实现headerview的显示和隐藏,从而达到下拉的效果

。当用户滑动到最底部时会触发加载更多的操作,此时会通过scroller滚动该下拉刷新组件,将footerview显示出来,实现加载更多

的效果。具体步骤如下:

第一步:初始化View即headerView contentView和footerView
第二步:测量三个view的大小,并计算出viewgroup的大小
第三步:布局,将三个view在界面上布局,按照上中下的顺序
第四步:监听屏幕的触摸事件,判断是否下拉刷新或者加载更多
第五步:触发下拉刷新和加载更多事件执行下拉刷新和加载更多
第六步:下拉刷新和加载更多执行完后的重置操作

示例代码:

自定义的viewgroup

 package com.jiao.simpleimageview.view;

 import android.content.Context;
import android.graphics.Color;
import android.support.v4.view.MotionEventCompat;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.RotateAnimation;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.Scroller;
import android.widget.TextView; import com.jiao.simpleimageview.R;
import com.jiao.simpleimageview.listener.OnLoadListener;
import com.jiao.simpleimageview.listener.OnRefreshListener; import java.text.SimpleDateFormat;
import java.util.Date; /**
* Created by jiaocg on 2016/3/24.
*/
public abstract class RefreshLayoutBase<T extends View> extends ViewGroup implements
OnScrollListener { /**
*
*/
protected Scroller mScroller; /**
* 下拉刷新时显示的header view
*/
protected View mHeaderView; /**
* 上拉加载更多时显示的footer view
*/
protected View mFooterView; /**
* 本次触摸滑动y坐标上的偏移量
*/
protected int mYOffset; /**
* 内容视图, 即用户触摸导致下拉刷新、上拉加载的主视图. 比如ListView, GridView等.
*/
protected T mContentView; /**
* 最初的滚动位置.第一次布局时滚动header的高度的距离
*/
protected int mInitScrollY = 0;
/**
* 最后一次触摸事件的y轴坐标
*/
protected int mLastY = 0; /**
* 空闲状态
*/
public static final int STATUS_IDLE = 0; /**
* 下拉或者上拉状态, 还没有到达可刷新的状态
*/
public static final int STATUS_PULL_TO_REFRESH = 1; /**
* 下拉或者上拉状态
*/
public static final int STATUS_RELEASE_TO_REFRESH = 2;
/**
* 刷新中
*/
public static final int STATUS_REFRESHING = 3; /**
* LOADING中
*/
public static final int STATUS_LOADING = 4; /**
* 当前状态
*/
protected int mCurrentStatus = STATUS_IDLE; /**
* header中的箭头图标
*/
private ImageView mArrowImageView;
/**
* 箭头是否向上
*/
private boolean isArrowUp;
/**
* header 中的文本标签
*/
private TextView mTipsTextView;
/**
* header中的时间标签
*/
private TextView mTimeTextView;
/**
* header中的进度条
*/
private ProgressBar mProgressBar;
/**
* 屏幕高度
*/
private int mScreenHeight;
/**
* Header 高度
*/
private int mHeaderHeight;
/**
* 下拉刷新监听器
*/
protected OnRefreshListener mOnRefreshListener;
/**
* 加载更多回调
*/
protected OnLoadListener mLoadListener; /**
* @param context
*/
public RefreshLayoutBase(Context context) {
this(context, null);
} /**
* @param context
* @param attrs
*/
public RefreshLayoutBase(Context context, AttributeSet attrs) {
this(context, attrs, 0);
} /**
* @param context
* @param attrs
* @param defStyle
*/
public RefreshLayoutBase(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs); // 初始化Scroller对象
mScroller = new Scroller(context); // 获取屏幕高度
mScreenHeight = context.getResources().getDisplayMetrics().heightPixels;
// header 的高度为屏幕高度的 1/4
mHeaderHeight = mScreenHeight / 4; // 初始化整个布局
initLayout(context);
} /**
* 第一步:初始化整个布局
*
* @param context
*/
private final void initLayout(Context context) {
// header view
setupHeaderView(context);
// 设置内容视图
setupContentView(context);
// 设置布局参数
setDefaultContentLayoutParams();
// 添加mContentView
addView(mContentView);
// footer view
setupFooterView(context); } /**
* 初始化 header view
*/
protected void setupHeaderView(Context context) {
mHeaderView = LayoutInflater.from(context).inflate(R.layout.pull_to_refresh_header, this,
false);
mHeaderView
.setLayoutParams(new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT,
mHeaderHeight));
mHeaderView.setBackgroundColor(Color.RED);
mHeaderView.setPadding(0, mHeaderHeight - 100, 0, 0);
addView(mHeaderView); // HEADER VIEWS
mArrowImageView = (ImageView) mHeaderView.findViewById(R.id.pull_to_arrow_image);
mTipsTextView = (TextView) mHeaderView.findViewById(R.id.pull_to_refresh_text);
mTimeTextView = (TextView) mHeaderView.findViewById(R.id.pull_to_refresh_updated_at);
mProgressBar = (ProgressBar) mHeaderView.findViewById(R.id.pull_to_refresh_progress);
} /**
* 初始化Content View, 子类覆写.
*/
protected abstract void setupContentView(Context context); /**
* 设置Content View的默认布局参数
*/
protected void setDefaultContentLayoutParams() {
ViewGroup.LayoutParams params =
new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT);
mContentView.setLayoutParams(params);
} /**
* 初始化footer view
*/
protected void setupFooterView(Context context) {
mFooterView = LayoutInflater.from(context).inflate(R.layout.pull_to_refresh_footer,
this, false);
addView(mFooterView);
} /**
* 第二步:测量
* 丈量视图的宽、高。宽度为用户设置的宽度,高度则为header,
* content view, footer这三个子控件的高度之和。
*
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
int childCount = getChildCount();
int finalHeight = 0;
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
// measure
measureChild(child, widthMeasureSpec, heightMeasureSpec);
// 该view所需要的总高度
finalHeight += child.getMeasuredHeight();
}
setMeasuredDimension(width, finalHeight);
} /**
* 第三步:布局
* 布局函数,将header, content view,
* footer这三个view从上到下布局。布局完成后通过Scroller滚动到header的底部,
* 即滚动距离为header的高度 +本视图的paddingTop,从而达到隐藏header的效果.
*/
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) { int childCount = getChildCount();
int top = getPaddingTop();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
child.layout(0, top, child.getMeasuredWidth(), child.getMeasuredHeight() + top);
top += child.getMeasuredHeight();
} // 计算初始化滑动的y轴距离
mInitScrollY = mHeaderView.getMeasuredHeight() + getPaddingTop();
// 滑动到header view高度的位置, 从而达到隐藏header view的效果
scrollTo(0, mInitScrollY);
} /**
* 第四步:监听滑动事件
* 与Scroller合作,实现平滑滚动。在该方法中调用Scroller的computeScrollOffset来判断滚动是否结束。
* 如果没有结束,
* 那么滚动到相应的位置,并且调用postInvalidate方法重绘界面,
* 从而再次进入到这个computeScroll流程,直到滚动结束。
*/
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
postInvalidate();
}
} /*
* 在适当的时候拦截触摸事件,这里指的适当的时候是当mContentView滑动到顶部,
* 并且是下拉时拦截触摸事件,否则不拦截,交给其child
* view 来处理。
*/
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) { final int action = MotionEventCompat.getActionMasked(ev);
// Always handle the case of the touch gesture being complete.
if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
// Do not intercept touch event, let the child handle it
return false;
} switch (action) { case MotionEvent.ACTION_DOWN:
mLastY = (int) ev.getRawY();
break; case MotionEvent.ACTION_MOVE:
// int yDistance = (int) ev.getRawY() - mYDown;
mYOffset = (int) ev.getRawY() - mLastY;
// 如果拉到了顶部, 并且是下拉,则拦截触摸事件,从而转到onTouchEvent来处理下拉刷新事件
if (isTop() && mYOffset > 0) {
return true;
}
break; }
// Do not intercept touch event, let the child handle it
return false;
} /**
* 第五步:下拉刷新
* 1、滑动view显示出headerview
* 2、进度条滚动,修改标题内容
* 3、执行下拉刷新监听
* 4、刷新成功或失败后重置:隐藏headerview 修改标题内容
* 在这里处理触摸事件以达到下拉刷新或者上拉自动加载的问题
*
* @see android.view.View#onTouchEvent(android.view.MotionEvent)
*/
@Override
public boolean onTouchEvent(MotionEvent event) {//下拉刷新的处理
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
int currentY = (int) event.getRawY();
mYOffset = currentY - mLastY;
if (mCurrentStatus != STATUS_LOADING) {
changeScrollY(mYOffset);
} rotateHeaderArrow();//旋转箭头
changeTips();//重置文本
mLastY = currentY;
break; case MotionEvent.ACTION_UP:
// 下拉刷新的具体操作
doRefresh();
break;
default:
break;
}
return true;
} /**
* 设置滚动的参数
*
* @param yOffset
*/
private void startScroll(int yOffset) {
mScroller.startScroll(getScrollX(), getScrollY(), 0, yOffset);
invalidate();
} /**
* y轴上滑动到指定位置
*
* @param distance
* @return
*/
protected void changeScrollY(int distance) {
// 最大值为 scrollY(header 隐藏), 最小值为0 ( header 完全显示).
int curY = getScrollY();
// 下拉
if (distance > 0 && curY - distance > getPaddingTop()) {
scrollBy(0, -distance);
} else if (distance < 0 && curY - distance <= mInitScrollY) {
// 上拉过程
scrollBy(0, -distance);
} curY = getScrollY();
int slop = mInitScrollY / 2;
//
if (curY > 0 && curY < slop) {
mCurrentStatus = STATUS_RELEASE_TO_REFRESH;
} else if (curY > 0 && curY > slop) {
mCurrentStatus = STATUS_PULL_TO_REFRESH;
}
} /**
* 旋转箭头图标
*/
protected void rotateHeaderArrow() { if (mCurrentStatus == STATUS_REFRESHING) {
return;
} else if (mCurrentStatus == STATUS_PULL_TO_REFRESH && !isArrowUp) {
return;
} else if (mCurrentStatus == STATUS_RELEASE_TO_REFRESH && isArrowUp) {
return;
} mProgressBar.setVisibility(View.GONE);
mArrowImageView.setVisibility(View.VISIBLE);
float pivotX = mArrowImageView.getWidth() / 2f;
float pivotY = mArrowImageView.getHeight() / 2f;
float fromDegrees = 0f;
float toDegrees = 0f;
if (mCurrentStatus == STATUS_PULL_TO_REFRESH) {
fromDegrees = 180f;
toDegrees = 360f;
} else if (mCurrentStatus == STATUS_RELEASE_TO_REFRESH) {
fromDegrees = 0f;
toDegrees = 180f;
} RotateAnimation animation = new RotateAnimation(fromDegrees, toDegrees, pivotX, pivotY);
animation.setDuration(100);
animation.setFillAfter(true);
mArrowImageView.startAnimation(animation); if (mCurrentStatus == STATUS_RELEASE_TO_REFRESH) {
isArrowUp = true;
} else {
isArrowUp = false;
}
} /**
* 根据当前状态修改header view中的文本标签
*/
protected void changeTips() {
if (mCurrentStatus == STATUS_PULL_TO_REFRESH) {
mTipsTextView.setText(R.string.pull_to_refresh_pull_label);
} else if (mCurrentStatus == STATUS_RELEASE_TO_REFRESH) {
mTipsTextView.setText(R.string.pull_to_refresh_release_label);
}
} /**
* 手指抬起时,根据用户下拉的高度来判断是否是有效的下拉刷新操作。
* 如果下拉的距离超过header view的
* 1/2那么则认为是有效的下拉刷新操作,否则恢复原来的视图状态.
*/
private void changeHeaderViewStaus() {
int curScrollY = getScrollY();
// 超过1/2则认为是有效的下拉刷新, 否则还原
if (curScrollY < mInitScrollY / 2) {
mScroller.startScroll(getScrollX(), curScrollY, 0, mHeaderView.getPaddingTop()
- curScrollY);
mCurrentStatus = STATUS_REFRESHING;
mTipsTextView.setText(R.string.pull_to_refresh_refreshing_label);
mArrowImageView.clearAnimation();
mArrowImageView.setVisibility(View.GONE);
mProgressBar.setVisibility(View.VISIBLE);
} else {
mScroller.startScroll(getScrollX(), curScrollY, 0, mInitScrollY - curScrollY);
mCurrentStatus = STATUS_IDLE;
} invalidate();
} /**
* 执行下拉刷新
*/
protected void doRefresh() {
changeHeaderViewStaus();
// 执行刷新操作
if (mCurrentStatus == STATUS_REFRESHING && mOnRefreshListener != null) {
mOnRefreshListener.onRefresh();
}
} /**
* 刷新结束,恢复状态
*/
public void refreshComplete() {
mScroller.startScroll(getScrollX(), getScrollY(), 0, mInitScrollY - getScrollY());
mCurrentStatus = STATUS_IDLE;
invalidate();
updateHeaderTimeStamp(); // 200毫秒后处理arrow和progressbar,免得太突兀
this.postDelayed(new Runnable() { @Override
public void run() {
mArrowImageView.setVisibility(View.VISIBLE);
mProgressBar.setVisibility(View.GONE);
}
}, 100); } /**
* 修改header上的最近更新时间
*/
private void updateHeaderTimeStamp() {
// 设置更新时间
mTimeTextView.setText(R.string.pull_to_refresh_update_time_label);
SimpleDateFormat sdf = (SimpleDateFormat) SimpleDateFormat.getInstance();
sdf.applyPattern("yyyy-MM-dd HH:mm:ss");
mTimeTextView.append(sdf.format(new Date()));
} /**
* 第六步:加载更多
* 滚动监听,当滚动到最底部,且用户设置了加载更多的监听器时触发加载更多操作.
* AbsListView, int, int, int)
*/
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
int totalItemCount) {
// 用户设置了加载更多监听器,且到了最底部,并且是上拉操作,那么执行加载更多.
if (mLoadListener != null && isBottom() && mScroller.getCurrY() <= mInitScrollY
&& mYOffset <= 0
&& mCurrentStatus == STATUS_IDLE) {
showFooterView();
doLoadMore();
}
} @Override
public void onScrollStateChanged(AbsListView view, int scrollState) { } /**
* 执行下拉(自动)加载更多的操作
*/
protected void doLoadMore() {
if (mLoadListener != null) {
mLoadListener.onLoadMore();
}
}
/**
* 显示footer view
*/
private void showFooterView() {
startScroll(mFooterView.getMeasuredHeight());
mCurrentStatus = STATUS_LOADING;
} /**
* 加载结束,恢复状态
*/
public void loadCompelte() {
// 隐藏footer
startScroll(mInitScrollY - getScrollY());
mCurrentStatus = STATUS_IDLE;
} /**
* 设置下拉刷新监听器
*
* @param listener
*/
public void setOnRefreshListener(OnRefreshListener listener) {
mOnRefreshListener = listener;
} /**
* 设置滑动到底部时自动加载更多的监听器
*
* @param listener
*/
public void setOnLoadListener(OnLoadListener listener) {
mLoadListener = listener;
} /**
* 是否已经到了最顶部,子类需覆写该方法,使得mContentView滑动到最顶端时返回true, 如果到达最顶端用户继续下拉则拦截事件;
*
* @return
*/
protected abstract boolean isTop(); /**
* 是否已经到了最底部,子类需覆写该方法,使得mContentView滑动到最底端时返回true;从而触发自动加载更多的操作
*
* @return
*/
protected abstract boolean isBottom(); /**
* 返回Content View
*
* @return
*/
public T getContentView() {
return mContentView;
} /**
* @return
*/
public View getHeaderView() {
return mHeaderView;
} /**
* @return
*/
public View getFooterView() {
return mFooterView;
} }

实现下拉刷新的listview

 package com.jiao.simpleimageview.view;

 import android.content.Context;
import android.util.AttributeSet;
import android.widget.ListAdapter;
import android.widget.ListView; /**
* Created by jiaocg on 2016/3/25.
*/
public class RefreshListView extends RefreshLayoutBase<ListView> {
/**
* @param context
*/
public RefreshListView(Context context) {
this(context, null);
} /**
* @param context
* @param attrs
*/
public RefreshListView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
} /**
* @param context
* @param attrs
* @param defStyle
*/
public RefreshListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
} @Override
protected void setupContentView(Context context) {
mContentView = new ListView(context);
// 设置滚动监听器
mContentView.setOnScrollListener(this); } @Override
protected boolean isTop() { //当第一个可见项是第一项时表示已经拉倒了顶部
return mContentView.getFirstVisiblePosition() == 0
&& getScrollY() <= mHeaderView.getMeasuredHeight();
} @Override
protected boolean isBottom() {
//当最后一个可见项是最后一项时表示已经拉倒了底部
return mContentView != null && mContentView.getAdapter() != null
&& mContentView.getLastVisiblePosition() ==
mContentView.getAdapter().getCount() - 1;
} /**
* 设置adapter
*/
public void setAdapter(ListAdapter adapter) {
mContentView.setAdapter(adapter);
} public ListAdapter getAdapter() {
return mContentView.getAdapter();
} }

然后直接在xml文件中引用使用即可实现,另外这种方式的下拉刷新扩展性很强

也可以实现TextView和GridView的刷新,只需继承该base实现其中的抽象方法即可

源码下载:https://yunpan.cn/cqKRSr2r2MsEk  提取密码:d177

Android自定义控件(二)的更多相关文章

  1. android 自定义控件二之仿QQ长按删除

    自定义Dialog 1.先上个效果图:

  2. android自定义控件(二) 入门,继承View

    转载请注明地址:http://blog.csdn.net/ethan_xue/article/details/7313788 ps: 可根据apidemo里LableView,list4,list6学 ...

  3. 老猪带你玩转android自定义控件二——自定义索引栏listview

    带索引栏的listview,在android开发非常普遍,方便用户进行字母索引,就像微信通讯录这样: 今天,我们就从零到一实现这个具有索引栏的listview. 怎么实现这个控件了,我们应当梳理出一个 ...

  4. 玩转android自定义控件二——自定义索引栏listview

    带索引栏的listview,在android开发非常普遍,方便用户进行字母索引,就像微信通讯录这样: 今天,我们就从零到一实现这个具有索引栏的listview. 怎么实现这个控件了,我们应当梳理出一个 ...

  5. Android自定义控件View(二)继承控件

    在前一篇博客中学习了Android自定义控件View的流程步骤和注意点,不了解的童鞋可以参考Android自定义控件View(一).这一节开始学习自定义控件View(二)之继承系统已有的控件.我们来自 ...

  6. Android自定义控件之自定义属性(二)

    前言: 上篇介绍了自定义控件的基本要求以及绘制的基本原理,本篇文章主要介绍如何给自定义控件自定义一些属性.本篇文章将继续以上篇文章自定义圆形百分比为例进行讲解.有关原理知识请参考Android自定义控 ...

  7. Android自定义控件之自定义ViewGroup实现标签云

    前言: 前面几篇讲了自定义控件绘制原理Android自定义控件之基本原理(一),自定义属性Android自定义控件之自定义属性(二),自定义组合控件Android自定义控件之自定义组合控件(三),常言 ...

  8. Android自定义控件之自定义组合控件

    前言: 前两篇介绍了自定义控件的基础原理Android自定义控件之基本原理(一).自定义属性Android自定义控件之自定义属性(二).今天重点介绍一下如何通过自定义组合控件来提高布局的复用,降低开发 ...

  9. Android自定义控件:进度条的四种实现方式(Progress Wheel的解析)

    最近一直在学习自定义控件,搜了许多大牛们Blog里分享的小教程,也上GitHub找了一些类似的控件进行学习.发现读起来都不太好懂,就想写这么一篇东西作为学习笔记吧. 一.控件介绍: 进度条在App中非 ...

  10. Android自定义控件 开源组件SlidingMenu的项目集成

    在实际项目开发中,定制一个菜单,能让用户得到更好的用户体验,诚然菜单的样式各种各样,但是有一种菜单——滑动菜单,是被众多应用广泛使用的.关于这种滑动菜单的实现,我在前面的博文中也介绍了如何自定义去实现 ...

随机推荐

  1. 引用类型-Function类型

    Function类型 定义函数的三种方式: 1.函数声明 function sum(num1,num2){ return num1 +num2; } 2.函数表达式 var sum = functio ...

  2. 关于SubSonic3.0插件使用SubSonic.Query.Select查询时,字段类型为tinyint时列丢失问题的Bug修复

    下午在写代码时,突然发现一个列名为Enable的字段怎么也查询不出来,开始以为可能这个名称是关键字,所以给过滤掉了,所以就将名称修改为IsEnable,问题还是一样......将名称又改为IsEnab ...

  3. Sybase数据库,普通表修改分区表步骤

    本文目标:指导项目侧人员再遇到此类改动需求时可以自己参照更改.需求:Sybase数据库,普通表t_jingyu修改为按天分区的分区表. 1.sp_help查看t_jingyu的表结构,索引等信息 sp ...

  4. 使用脚本操作UpdatePanel中控件的问题

    假设有一个脚本(用js或者jQuery等类似手段编写),为UpdatePanel中的一个普通的TextBox赋值.如果你以为这样写: <head runat="server" ...

  5. 小白Linux入门 二

    参考: http://edu.51cto.com/lesson/id-11222.html CPU中有计算单元 控制单元.它通过桥接芯片与存储器进行匹配 其中北桥是高速 南桥是低速 包括IDE USB ...

  6. MySQL数据迁移到SQL Server

    数据迁移的工具有很多,基本SSMA团队已经考虑到其他数据库到SQL Server迁移的需求了,所以已经开发了相关的迁移工具来支持. 此博客主要介绍MySQL到SQL Server数据迁移的工具:SQL ...

  7. CSS padding margin border属性详解

    图解CSS padding.margin.border属性W3C组织建议把所有网页上的对像都放在一个盒(box)中,设计师可以通过创建定义来控制这个盒的属性,这些对像包括段落.列表.标题.图片以及层. ...

  8. 创建ASP.NET Core MVC应用程序(1)-添加Controller和View

    创建ASP.NET Core MVC应用程序(1)-添加Controller和View 参考文档:Getting started with ASP.NET Core MVC and Visual St ...

  9. 【原创】Kafka console consumer源代码分析(二)

    我们继续讨论console consumer的实现原理,本篇着重探讨ZookeeperConsumerConnector的使用,即后续所有的内容都由下面这条语句而起: val connector = ...

  10. rsa互通密钥对生成及互通加解密(c#,java,php)

    摘要 在数据安全上rsa起着非常大的作用,特别是数据网络通讯的安全上.当异构系统在数据网络通讯上对安全性有所要求时,rsa将作为其中的一种选择,此时rsa的互通性就显得尤为重要了. 本文参考网络资料, ...