ipaulpro / android-itemtouchhelper-demo Goto Github PK
View Code? Open in Web Editor NEWBasic example of using ItemTouchHelper to add drag & drop and swipe-to-dismiss to RecyclerView.
License: Apache License 2.0
Basic example of using ItemTouchHelper to add drag & drop and swipe-to-dismiss to RecyclerView.
License: Apache License 2.0
Hi Paul, thanks for the example it's really great. I wanted to ask if is it possible to add button under the swiped item (for removing items). I need extra level of security before removing the item (like delete button under the item and dialog box after the button is pressed before removing item). I've tried to do this by puting relativelayout with button in the same item_main.xml file under framelayout (with handle and textview) and move only framelayout (with handle and textview) while swiping, so after the swipe layout with button is visible. But it didn't work, when I tried to press the button, framelayout (with handle and textview) would pop up back to in to place and button wouldn't register any events.
immediately after delete button was pressed
I would be really grateful if you could show an example of under swipeable button.
And keep up the great work.
Use this layout to see this issue then try and drag the last item in the list/grid upwards to see it jump several positions:
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
android:clipToPadding="false"
android:paddingBottom="150dp"
android:paddingTop="150dp" />
As you said at your Medium post, we’re changing the position of the item every time the view is shifted to a new index, and not at the end of a “drop” event.
So, would it be possible to add a listener or to be notified at the end of a drop event in order to make changes to the layout or to store those changes in a DB?
Please consider adding the ability to limit the range of items that can be dragged.
e.g to limit dragging of an item only within its own "group"
I wonder if I can move my items to another layout. I split the screen to two, left with a FrameLayout and right with a RecyclerView. I want to be able to move items from RecylerView and put them on other layout by dragging, and vice versa. I could not find a solution about it, currently dragging items is only available in RecyclerView, so I cannot drag the item to left or right, I can only drag it to up or down. Any suggestions would be appreciated.
I've implemented your method into my project, and i have a few problems, hopefully i will explain it properly:
getAdapterPosition()
of adapter in each item, it is working nice until i start "sling shoting" items via drag and sometimes there is a duplicate of a position ( 1 , 2 , 3 , 5 ,5 ,6 ,7) i fixed that issue with notifyDataSetChanged
in onItemClear()
but that wasn't very fortunate since i get IllegalStateException
if i do the same thing.I know this may be a bit difficult to resolve (cant use debugger since i have some huge recursion before that code which takes forever) and those lists can be really tricky to debug, so any tip or hint would be awesome. Thank you in advance.
when I deleted a item and scrolling screen,something wrong。。。。,I am a chinese boy, my English....
you can use those code ,try it,you will see it,thank you very much
public class MainActivity extends Activity {
private RecyclerView mRecyclerView;
private ItemTouchHelper mItemTouchHelper;
@OverRide
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RecyclerListAdapter adapter = new RecyclerListAdapter(this);
mRecyclerView = (RecyclerView) findViewById(R.id.mRecyclerView);
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setAdapter(adapter);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
ItemTouchHelper.Callback callback = new SimpleItemTouchHelperCallback(adapter);
mItemTouchHelper = new ItemTouchHelper(callback);
mItemTouchHelper.attachToRecyclerView(mRecyclerView);
}
}
public class RecyclerListAdapter extends RecyclerView.Adapter<RecyclerListAdapter.ItemViewHolder>
implements ItemTouchHelperAdapter {
private final List<String> mItems = new ArrayList<>();
public RecyclerListAdapter(Context context) {
mItems.addAll(Arrays.asList(context.getResources().getStringArray(R.array.dummy_items)));
}
@Override
public ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_main, parent, false);
return new ItemViewHolder(view);
}
@Override
public void onBindViewHolder(final ItemViewHolder holder, int position) {
holder.textView.setText(mItems.get(position));
}
@Override
public void onItemDismiss(int position) {
mItems.remove(position);
notifyItemRemoved(position);
}
@Override
public boolean onItemMove(int fromPosition, int toPosition) {
Collections.swap(mItems, fromPosition, toPosition);
notifyItemMoved(fromPosition, toPosition);
return true;
}
@Override
public int getItemCount() {
return mItems.size();
}
/**
* Simple example of a view holder that implements {@link ItemTouchHelperViewHolder} and has a
* "handle" view that initiates a drag event when touched.
*/
public static class ItemViewHolder extends RecyclerView.ViewHolder {
public final TextView textView;
public final ImageView handleView;
public ItemViewHolder(View itemView) {
super(itemView);
textView = (TextView) itemView.findViewById(R.id.text);
handleView = (ImageView) itemView.findViewById(R.id.handle);
}
}
@OverRide
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
// Set movement flags based on the layout manager
if (recyclerView.getLayoutManager() instanceof GridLayoutManager) {
final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
final int swipeFlags = 0;
return makeMovementFlags(dragFlags, swipeFlags);
} else if (viewHolder instanceof ItemTouchHelperViewHolder) {
//Only ViewHolder implents ItemTouchHelperViewHolder can be draged.
final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
final int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
return makeMovementFlags(dragFlags, swipeFlags);
} else {
//others can not be draged.
final int swipeFlags = 0;
final int dragFlags = 0;
return makeMovementFlags(dragFlags, swipeFlags);
}
}
What am I doing wrong that I am getting continual onMove calls while dragging a row up and down. I just want to get the final FROM and TO?
Hello. Thanks for great library.
I need to disable swipe item cell. Please help
Thanks for this great demo
I have an issue while i'm using it with GridLayout manager that the item keeps there old positions
e.g. if i moved an item from position 0 to position 3 when i click on it and print out the position it will give me 0 also
even tho i added this snippet which is can be related to this issue
public boolean onItemMove(int fromPosition, int toPosition) { if (fromPosition < toPosition) { for (int i = fromPosition; i < toPosition; i++) { Collections.swap(mItems, i, i + 1); } } else { for (int i = fromPosition; i > toPosition; i--) { Collections.swap(mItems, i, i - 1); } } notifyItemMoved(fromPosition, toPosition); return true; }
Thanks
Does anyone know how I can detect threshold on swipe. Also how to control the reverse animation. Also how do I attached swipe to children of view holder instead of the view holder itself
Hi, just downloaded the sample app and am still reading through the article. Wanted to ask can you demo how to add icons just like in google inbox or in dropbox's mailbox. It's what I actually need for my use case.
Thanks for this. The solutions out there are overwhelming so when I saw this article I was so relieved.
Lets say I have 10 items in my recyclerview and item 4 is a different viewtype then the rest of the items. When dragging say item 0 across the recyclerview, there is a fade in and out effect on item 4 and that's because we don't swap elements with different viewtypes. Is there a way to avoid "refreshing" the adapter when we drag across different viewtypes?
Hello,
In case of GridLayoutManager, it's possible to move an item from a row to another. If the number of columns is superior to 1, we graphically swap more than one item and this case is not correctly handled by adapter.
A |
B |
---|---|
C |
B | C |
---|---|
A |
Current implementation will just swap A and C, mItems will be equal to :
item[0] = C
item[1] = B
item[2] = A
Which is not good.
It corresponds to receive onItemMove() event with from/to having index distance of 2 or more. In my example: from = 0 and to = 2.
I handled this case like this :
@Override
public boolean onItemMove(int fromPosition, int toPosition) {
if (fromPosition < toPosition) {
for (int i = fromPosition; i < toPosition; i++) {
Collections.swap(mItems, i, i + 1);
notifyItemMoved(i, i + 1);
}
} else {
for (int i = fromPosition; i > toPosition; i--) {
Collections.swap(mItems, i, i - 1);
notifyItemMoved(i, i - 1);
}
}
return true;
}
I'm not fully sure it covers correctly all cases, other eyes will be appreciated.
Anthony Skrzypczyk.
Hello, I know how to disable drag and drop for specific type of ViewHolder at all, but how can I control it's "on/off" state from activity?
I have been wondering if I can add a zoom animation to my items on RecyclerView. My item layout has only ImageView and I want to be able to enlarge image to its original size on touch and return it to its scaled size after touch ends. I have tried several stuff but could not manage to find a solution. It either blocks the dragging or does not go back to scaled size after I stop pressing.
If I add a touch listener with ACTION_DOWN and ACTION_UP cases, I can use a scale animation but can't move the item. If I do it in onMove method, it enlarges after the drag, not on touch and it doesn't shrink back. Any suggestion?
This is strange, but no shadow drawn on Lollipop, even being elevated (see ItemTouchUIUtilImpl
), it has no effect.
**********/helper/SimpleItemTouchHelperCallback.java
Error:(34, 8) error: cannot access ScrollingView
class file for android.support.v4.view.ScrollingView not found
Error:(44, 5) error: method does not override or implement a method from a supertype
Error:(49, 5) error: method does not override or implement a method from a supertype
Error:(54, 5) error: method does not override or implement a method from a supertype
Error:(57, 25) error: cannot access NestedScrollingChild
class file for android.support.v4.view.NestedScrollingChild not found
Unfortunately, I've been unable to make this work with multiple viewTypes. It appears that the RecyclerView scraps differing viewTypes when they are dragged over each other, which cancels the re-ordering process. Any ideas on how to fix this?
Hey Paul,
Great demo (although it doesn't compile for me, I could use the classes and get it to work in my app). But I have a question. Is it possible to see the selected item (the one which you are dragging) also outside the RecyclerView.
I don't want to drop it there but it is possible that with dragging I will pass my recyclerview bounds and then the item will be cut off or even become invisible.
Is this possible using the ItemTouchHandler or should I do this in another way?
Another solution can be to use a DrawShadow which can be moved around the screen and use the ItemTouchHandler to do the reordering.
Kind regards,
JKorsten
Hello Paul,
so in my app I want users to be able to delete cells that they created. so for example in a group chat you can only delete a message you create. so swipe to dismiss should be disabled for messages which the user didn't create.
Whats the best way to approach this?
or some methods should be invoked in ItemTouchHelper?Can you understand what I'm saying? i have little confidence in my english.
I want to detect it is click or drag image. if I click then my camera open and if I drag the image then image will drag but in my case camera and drag image together.
please tell me how I resolved this issue.
Thanks
I noticed your medium post and I think it was a great tutorial on how to use the Android supported ItemTouchHelper for a recyclerview. I'm not sure if you have this planned in the future, but if you can demonstrate how to replace the default swipe animation that comes with an ItemTouchHelper with your own custom view animation, I feel this tutorial will be even more complete.
Сhanges in lists Collections.swap(mItems, fromPosition, toPosition) and notifyItemMoved(fromPosition, toPosition) not the same.
You can check it by calling recyclerView.getAdapter().notifyDataSetChanged(); in SimpleItemTouchHelperCallback#clearView()
My simple solution is:
String item = mItems.get(fromPosition);
expenditures.remove(fromPosition);
expenditures.add(toPosition, item);
RecyclerView allows it, including the class you use.
Only the special Alpha functions require API 13 and above.
I have a recycler view with grid layout and the first item is taking the full row,
|------item 1------|
| item 2 | item 3 |
| item 4 | item 5 |
the rest is swappable normally but i cannot swap with the first item, any solution?
If you update your items every 10 mills(in my case) onMove method will be called one time per 5 seconds.
in onItemClear of the ViewHolder, better to add if (getAdapterPosition() > -1) itemview. setBackgroundColor(color); Just in case someone like me want to use different color according to adapter position.
This isn't a bug with this code, but I'm throwing it out there as a potential gotcha that I only ran into after implementing the dismiss/swap functionality from this demo.
After a dismiss or swap of items in a recycler view, "holder.getAdapterPosition()" will return NO_POSITION (-1) if you've called "notifyDataSetChanged()" since the last layout pass. I was passing getAdapterPosition() to my Activity in response to some user interaction, which had always worked previously. After either of the actions above, the adapter position became invalid until an orientation change or attempt to scroll the screen. Using getLayoutPosition() in place of getAdapterPosition() resolved the issue.
If you update build.grade to use API 23 and associated dependencies, when swiping an item from the recycler view a gap can then appear when scrolling.
Steps to reproduce:
<array name="dummy_items">
to give a bigger scrolling listapply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
defaultConfig {
applicationId "co.paulburke.android.itemtouchhelperdemo"
minSdkVersion 16
targetSdkVersion 23
versionCode 3
versionName "1.1"
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:design:23.1.0'
compile 'com.android.support:recyclerview-v7:23.1.0'
}
I want to store item into folder on dropping that item onto folder . so Is there any way around to get my background view at the time of dragging ? If there is a folder i want to store that item into folder . So i have to detect what is in the background at the time of dragging .. Is it possible by ItemTouchHelper ?
If you add a spinner to each list row, opening and closing that spinner puts the row as the 'selected' row. Now scrolling the list will drag that row.
I need to implement the drag and drop for items with different view types. I tried with following code
override fun onMove(recyclerView: RecyclerView, source: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
// Notify the adapter of the move
mAdapter.onItemMove(source.adapterPosition, target.adapterPosition)
return true
}
without
if (source.itemViewType != target.itemViewType) {
return false
}
but it doesn't work as I expected. I'm using a grid layout by the way.
is there any way to delete item permanetly when swipe...the particualr item will not come again in list...
How do I get onDragEnd event or something similar?
Thanks
Hi, I'm really struggled to figure out how can I fix the recyclerview layout after moving. My recyclerview adapter has a head view at the position zero which is full spanned. While my grid has 3 columns. How can I resize the head view and the other item after being moved. Thanks for your help!
I've noticed that you have to drag an item almost exactly on top of a neighboring item before it will start the swap animation. There is a method called getMoveThreshold() that by default returns 0.5 which seems to suggest that you need to drag just half of the item's size before the swap animation kicks in. I tried returning lower thresholds in getMoveThreshold() but that has no effect.
Since my drag and drop is working fine but still the item which has been moved is not updating its position. The code is like this :
public class EditVideoActivity extends AppCompatActivity implements OnStartDragListener{
private ImageButton filterButton,cutButton;
//audioButton;
ArrayList<File> mList;
ArrayList<File> files;
private static int position = 0;
RecyclerView recyclerView;
private String selectedVideo;
private int selectedPos;
ViewAdapter viewAdapter;
RecyclerView.LayoutManager layoutManager;
private VideoView mVideoView;
private MediaController mController;
private Project project;
private ItemTouchHelper itemTouchHelper;
//private static final int CustomizeAudio = 1;
private static final int CustomizeFilter = 2;
private static final int CustomCutvideo = 3;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.activity_edit_video);
getSupportActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
getSupportActionBar().setCustomView(R.layout.edit_header_layout);
getSupportActionBar().setHomeAsUpIndicator(R.mipmap.ic_keyboard_arrow_left_black_24dp);
getSupportActionBar().setDisplayShowHomeEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
mVideoView = (VideoView) findViewById(R.id.editVideoPlayer);
filterButton = (ImageButton) findViewById(R.id.filterVideo);
cutButton = (ImageButton) findViewById(R.id.cutVideo);
//audioButton = (ImageButton) findViewById(R.id.musicalNotes);
//change the color of the imagebutton explicitely
filterButton.setColorFilter(Color.BLACK);
cutButton.setColorFilter(Color.BLACK);
//audioButton.setColorFilter(Color.BLACK);
recyclerView = (RecyclerView) findViewById(R.id.videoGallery);
recyclerView.setHasFixedSize(true);
mList = new ArrayList<>();
int view = getIntent().getIntExtra("view", 0);
//this is for admin activity else addfragment
if(view == 1){
project = (Project) getIntent().getSerializableExtra("groupVideoData");
Log.e("PROJECT_ID", String.valueOf(project.getId()));
Log.e("PROJECT_DATA", String.valueOf(project.getVideoData().size()));
for(String adminPath : project.getVideoData()){
File files = new File(adminPath);
mList.add(files);
}
//inflating the layout horizontally
layoutManager = new LinearLayoutManager(getParent(), LinearLayoutManager.HORIZONTAL, false);
recyclerView.setLayoutManager(layoutManager);
viewAdapter = new ViewAdapter(mList);
Log.e("LIST====", mList.toString());
recyclerView.setAdapter(viewAdapter);
recyclerView.setVisibility(View.VISIBLE);
ItemTouchHelper.Callback callback = new SimpleItemTouchHelperCallBack(viewAdapter);
itemTouchHelper = new ItemTouchHelper(callback);
itemTouchHelper.attachToRecyclerView(recyclerView);
}else {
//getting the value from project class object
project = (Project) getIntent().getSerializableExtra("passedData");
Log.e("project id====", String.valueOf(project.getId()));
Log.e("project data =====", String.valueOf(project.getVideoData().size()));
//Log.e("RECEIVED DATA=====", receivedData.toString());
for (String path : project.getVideoData()) {
//initialising path with file
File file = new File(path);
Log.e("FILENAME===", path);
mList.add(file); //adding file in array list<file> object
}
//inflating the layout horizontally
layoutManager = new LinearLayoutManager(getParent(), LinearLayoutManager.HORIZONTAL, false);
recyclerView.setLayoutManager(layoutManager);
viewAdapter = new ViewAdapter(mList);
Log.e("LIST====", mList.toString());
recyclerView.setAdapter(viewAdapter);
recyclerView.setVisibility(View.VISIBLE);
ItemTouchHelper.Callback callback = new SimpleItemTouchHelperCallBack(viewAdapter);
itemTouchHelper = new ItemTouchHelper(callback);
itemTouchHelper.attachToRecyclerView(recyclerView);
}
}
//just for a change
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.edit_video, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
finish();
return true;
case R.id.moveNext:
Intent intent = new Intent(EditVideoActivity.this,PreviewActivity.class);
//sending the file in string array list form to avoid mismatch of the file to string
//conversion
ArrayList<String> files = new ArrayList<>();
for(File file : mList){
files.add(file.getAbsolutePath());
}
intent.putStringArrayListExtra("videosList",files);
intent.putExtra("videos", project);
startActivity(intent);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public void onBackPressed() {
moveTaskToBack(false);
}
@Override
public void onDragStart(RecyclerView.ViewHolder viewHolder) {
itemTouchHelper.startDrag(viewHolder);
}`
and my RecyclerViewAdapter is here . :
public class ViewAdapter extends RecyclerView.Adapter<ViewAdapter.ViewHolder> implements
ItemTouchHelperAdapter{
```
private View rootView;
private Bitmap bitmap;
//calling the mList in constructor's argument item
public ViewAdapter(ArrayList<File> items) {
files = items;
Log.e("ITEMS====",items.toString());
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Log.e("ADAPTER","VIEW HOLDER CREATED");
Context context = parent.getContext();
LayoutInflater inflater = LayoutInflater.from(context);
rootView = inflater.inflate(R.layout.custom_listview,parent,false);
ViewHolder viewholder = new ViewHolder(rootView);
return viewholder;
}
@Override
public void onBindViewHolder( ViewHolder holder, final int position) {
Log.e("ADAPTER",files.get(position).toString());
EditVideoActivity.position = position;
if (files != null) {
bitmap = ThumbnailUtils.createVideoThumbnail(files.get(position).toString(),1);
holder.mImageView.setImageBitmap(bitmap);
//click event from the holder
holder.mImageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//getting the details of the selected video
updatedVideo();
Log.e("POS_SELECTED", String.valueOf(files.indexOf(files.get(position))));
}
});
}
}
@Override
public int getItemCount() {
return files.size();
}
@Override
public boolean onItemMove(int fromPosition, int toPosition) {
if (fromPosition < toPosition) {
for (int i = fromPosition; i < toPosition; i++) {
Collections.swap(files, i, i + 1);
}
} else {
for (int i = fromPosition; i > toPosition; i--) {
Collections.swap(files, i, i - 1);
}
}
notifyItemMoved(fromPosition, toPosition);
return true;
}
@Override
public void onItemDismiss(int position) {
}
//holder holding the image object from the custom listView
class ViewHolder extends RecyclerView.ViewHolder {
public ImageView mImageView;
public String videoPosition;
public int videoPos;
public ViewHolder(View view) {
super(view);
mImageView = (ImageView) view.findViewById(R.id.imageView);
// ----------- FILTER EDITING -----------------
filterButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
videoPosition = selectedVideo;
videoPos = selectedPos;
if(videoPosition != null){
Intent intent = new Intent(EditVideoActivity.this, FilterActivity.class);
intent.putExtra("videoData",videoPosition);
intent.putExtra("position",selectedPos); //sending the data of the position
Log.e("VIDEO_SENT_DATA=======", videoPosition);
Log.e("POSITION===",String.valueOf(selectedPos));
startActivityForResult(intent,CustomizeFilter);
}else
Toast.makeText(EditVideoActivity.this,"You have not selected any video",Toast.LENGTH_LONG)
.show();
}
});
// --------------- CUT VIDEO ----------------
cutButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
videoPosition = selectedVideo;
videoPos = selectedPos;
if(videoPosition != null){
Intent intent = new Intent(EditVideoActivity.this,CutVideoActivity.class);
intent.putExtra("videoData",videoPosition);
intent.putExtra("position",selectedPos); //sending the data of the position
Log.e("VIDEO_SENT_DATA=======", videoPosition);
Log.e("POSITION===",String.valueOf(selectedPos));
startActivityForResult(intent,CustomCutvideo);
}else
Toast.makeText(EditVideoActivity.this,"You have not selected any video",Toast.LENGTH_LONG)
.show();
}
});
}
}
}
private void updatedVideo() {
//getting video position for passing the data
selectedVideo = files.get(position).toString();
//getting the position of the selected one
selectedPos = files.indexOf(files.get(position));
//for starting the video activity
mVideoView.setVideoPath(files.get(position).toString());
mController = new MediaController(EditVideoActivity.this);
mController.setMediaPlayer(mVideoView);
mVideoView.setMediaController(mController);
mVideoView.requestFocus();
mVideoView.start();
}`
In my logcat if I select the first one that is at pos = 0 then after dragging to pos = 1 and selecting the same again it will give the selected pos = 0 only.
Do you know how to get this working with stable ids?
Can someone give me a hint how to implement a toggle between "normalmode" (tab to select an item) and "editmode" (drag & swipe)?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.